From 0fa642d94934f8e5974f2f5b2b2bf65fc32fbe4c Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 27 Aug 2013 16:58:10 +0400 Subject: [PATCH] Require uuid + move functor_task to task.py Require details have a uuid of an originating runner, remove tasks having there own uuid since only when a task is added to a flow does it obtain a uuid. Move functor_task to task.py since it is task releated and seems better connected in task.py instead of as its own module. Change-Id: I8c441e184afcdd697d077f166f2e550fcafcd385 --- taskflow/decorators.py | 4 +- taskflow/functor_task.py | 95 ------------------------ taskflow/persistence/flowdetail.py | 8 +- taskflow/persistence/taskdetail.py | 15 +--- taskflow/task.py | 83 ++++++++++++++++++--- taskflow/tests/unit/persistence/base.py | 16 ++-- taskflow/tests/unit/test_functor_task.py | 8 +- 7 files changed, 92 insertions(+), 137 deletions(-) delete mode 100644 taskflow/functor_task.py diff --git a/taskflow/decorators.py b/taskflow/decorators.py index 45f4098f..1c5c0734 100644 --- a/taskflow/decorators.py +++ b/taskflow/decorators.py @@ -18,7 +18,7 @@ import functools -from taskflow import functor_task +from taskflow import task as base from taskflow import utils @@ -78,7 +78,7 @@ def task(*args, **kwargs): # NOTE(imelnikov): we can't capture f here because for # bound methods and bound class methods the object it # is bound to is yet unknown at the moment - return functor_task.FunctorTask(execute_with, **merged) + return base.FunctorTask(execute_with, **merged) w_f = _original_function(f) setattr(w_f, utils.TASK_FACTORY_ATTRIBUTE, task_factory) return f diff --git a/taskflow/functor_task.py b/taskflow/functor_task.py deleted file mode 100644 index bda3e670..00000000 --- a/taskflow/functor_task.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- - -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012-2013 Yahoo! Inc. All Rights Reserved. -# Copyright (C) 2013 AT&T Labs Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import inspect - -from taskflow import task as base - -# These arguments are ones that we will skip when parsing for requirements -# for a function to operate (when used as a task). -AUTO_ARGS = ('self', 'context', 'cls') - - -def _filter_arg(arg): - if arg in AUTO_ARGS: - return False - # In certain decorator cases it seems like we get the function to be - # decorated as an argument, we don't want to take that as a real argument. - if not isinstance(arg, basestring): - return False - return True - - -class FunctorTask(base.Task): - """Adaptor to make task from a callable - - Take any callable and make a task from it. - """ - @staticmethod - def _get_callable_name(execute_with): - """Generate a name from callable""" - im_class = getattr(execute_with, 'im_class', None) - if im_class is not None: - parts = (im_class.__module__, im_class.__name__, - execute_with.__name__) - else: - parts = (execute_with.__module__, execute_with.__name__) - return '.'.join(parts) - - def __init__(self, execute_with, **kwargs): - """Initialize FunctorTask instance with given callable and kwargs - - :param execute_with: the callable - :param kwargs: reserved keywords (all optional) are - name: name of the task, default None (auto generate) - task_id: id of the task, default None (auto generate) - revert_with: the callable to revert, default None - version: version of the task, default Task's version 1.0 - optionals: optionals of the task, default () - provides: provides of the task, default () - requires: requires of the task, default () - auto_extract: auto extract execute_with's args and put it into - requires, default True - """ - name = kwargs.pop('name', None) - task_id = kwargs.pop('task_id', None) - if name is None: - name = self._get_callable_name(execute_with) - super(FunctorTask, self).__init__(name, task_id) - self._execute_with = execute_with - self._revert_with = kwargs.pop('revert_with', None) - self.version = kwargs.pop('version', self.version) - self.optional.update(kwargs.pop('optional', ())) - self.provides.update(kwargs.pop('provides', ())) - self.requires.update(kwargs.pop('requires', ())) - if kwargs.pop('auto_extract', True): - f_args = inspect.getargspec(execute_with).args - self.requires.update([arg for arg in f_args if _filter_arg(arg)]) - if kwargs: - raise TypeError('__init__() got an unexpected keyword argument %r' - % kwargs.keys[0]) - - def __call__(self, *args, **kwargs): - return self._execute_with(*args, **kwargs) - - def revert(self, *args, **kwargs): - if self._revert_with: - return self._revert_with(*args, **kwargs) - else: - return None diff --git a/taskflow/persistence/flowdetail.py b/taskflow/persistence/flowdetail.py index 7e5c8eee..75f27f21 100644 --- a/taskflow/persistence/flowdetail.py +++ b/taskflow/persistence/flowdetail.py @@ -16,7 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. -from taskflow.openstack.common import uuidutils from taskflow.persistence.backends import api as b_api @@ -29,11 +28,8 @@ class FlowDetail(object): persisted when the logbook that contains this flow detail is saved or when the save() method is called directly. """ - def __init__(self, name, uuid=None, backend='memory'): - if uuid: - self._uuid = uuid - else: - self._uuid = uuidutils.generate_uuid() + def __init__(self, name, uuid, backend='memory'): + self._uuid = uuid self._name = name self._taskdetails = [] self.state = None diff --git a/taskflow/persistence/taskdetail.py b/taskflow/persistence/taskdetail.py index 836a8c16..ba135c99 100644 --- a/taskflow/persistence/taskdetail.py +++ b/taskflow/persistence/taskdetail.py @@ -16,7 +16,6 @@ # License for the specific language governing permissions and limitations # under the License. -from taskflow.openstack.common import uuidutils from taskflow.persistence.backends import api as b_api @@ -33,12 +32,10 @@ class TaskDetail(object): persisted when the logbook that contains this task detail is saved or when the save() method is called directly. """ - def __init__(self, name, task=None, uuid=None, backend='memory'): - if uuid: - self._uuid = uuid - else: - self._uuid = uuidutils.generate_uuid() + def __init__(self, name, uuid, backend='memory'): + self._uuid = uuid self._name = name + self.backend = backend # TODO(harlowja): decide if these should be passed in and therefore # immutable or let them be assigned? # @@ -60,12 +57,6 @@ class TaskDetail(object): # is quite useful for determining what versions of tasks this detail # information can be associated with. self.version = None - if task and task.version: - if isinstance(task.version, basestring): - self.version = str(task.version) - elif isinstance(task.version, (tuple, list)): - self.version = '.'.join([str(p) for p in task.version]) - self.backend = backend def save(self): """Saves *most* of the components of this given object. diff --git a/taskflow/task.py b/taskflow/task.py index c179e82f..82d667aa 100644 --- a/taskflow/task.py +++ b/taskflow/task.py @@ -17,10 +17,14 @@ # under the License. import abc +import inspect -from taskflow.openstack.common import uuidutils from taskflow import utils +# These arguments are ones that we will skip when parsing for requirements +# for a function to operate (when used as a task). +AUTO_ARGS = ('self', 'context', 'cls') + class Task(object): """An abstraction that defines a potential piece of work that can be @@ -28,11 +32,7 @@ class Task(object): """ __metaclass__ = abc.ABCMeta - def __init__(self, name, task_id=None): - if task_id: - self._uuid = task_id - else: - self._uuid = uuidutils.generate_uuid() + def __init__(self, name): self._name = name # An *immutable* input 'resource' name set this task depends # on existing before this task can be applied. @@ -49,10 +49,6 @@ class Task(object): # major, minor version semantics apply. self.version = (1, 0) - @property - def uuid(self): - return self._uuid - @property def name(self): return self._name @@ -75,3 +71,70 @@ class Task(object): provided as well as any information which may have caused said reversion. """ + + +def _filter_arg(arg): + if arg in AUTO_ARGS: + return False + # In certain decorator cases it seems like we get the function to be + # decorated as an argument, we don't want to take that as a real argument. + if not isinstance(arg, basestring): + return False + return True + + +class FunctorTask(Task): + """Adaptor to make task from a callable + + Take any callable and make a task from it. + """ + @staticmethod + def _get_callable_name(execute_with): + """Generate a name from callable""" + im_class = getattr(execute_with, 'im_class', None) + if im_class is not None: + parts = (im_class.__module__, im_class.__name__, + execute_with.__name__) + else: + parts = (execute_with.__module__, execute_with.__name__) + return '.'.join(parts) + + def __init__(self, execute_with, **kwargs): + """Initialize FunctorTask instance with given callable and kwargs + + :param execute_with: the callable + :param kwargs: reserved keywords (all optional) are + name: name of the task, default None (auto generate) + revert_with: the callable to revert, default None + version: version of the task, default Task's version 1.0 + optionals: optionals of the task, default () + provides: provides of the task, default () + requires: requires of the task, default () + auto_extract: auto extract execute_with's args and put it into + requires, default True + """ + name = kwargs.pop('name', None) + if name is None: + name = self._get_callable_name(execute_with) + super(FunctorTask, self).__init__(name) + self._execute_with = execute_with + self._revert_with = kwargs.pop('revert_with', None) + self.version = kwargs.pop('version', self.version) + self.optional.update(kwargs.pop('optional', ())) + self.provides.update(kwargs.pop('provides', ())) + self.requires.update(kwargs.pop('requires', ())) + if kwargs.pop('auto_extract', True): + f_args = inspect.getargspec(execute_with).args + self.requires.update([arg for arg in f_args if _filter_arg(arg)]) + if kwargs: + raise TypeError('__init__() got an unexpected keyword argument %r' + % kwargs.keys[0]) + + def __call__(self, *args, **kwargs): + return self._execute_with(*args, **kwargs) + + def revert(self, *args, **kwargs): + if self._revert_with: + return self._revert_with(*args, **kwargs) + else: + return None diff --git a/taskflow/tests/unit/persistence/base.py b/taskflow/tests/unit/persistence/base.py index 270b1b30..f5efdf8c 100644 --- a/taskflow/tests/unit/persistence/base.py +++ b/taskflow/tests/unit/persistence/base.py @@ -56,7 +56,7 @@ class PersistenceTestMixin(object): lb = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test') + fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid()) lb.add(fd) # Ensure we can't save it since its owning logbook hasn't been @@ -73,9 +73,9 @@ class PersistenceTestMixin(object): lb = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test') + fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid()) lb.add(fd) - td = taskdetail.TaskDetail("detail-1") + td = taskdetail.TaskDetail("detail-1", uuid=uuidutils.generate_uuid()) fd.add(td) # Ensure we can't save it since its owning logbook hasn't been @@ -94,13 +94,13 @@ class PersistenceTestMixin(object): lb = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test') + fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid()) lb.add(fd) lb.save() lb2 = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test2') + fd = flowdetail.FlowDetail('test2', uuid=uuidutils.generate_uuid()) lb2.add(fd) lb2.save() @@ -113,7 +113,7 @@ class PersistenceTestMixin(object): lb = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test') + fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid()) lb.add(fd) lb.save() @@ -129,8 +129,8 @@ class PersistenceTestMixin(object): lb = logbook.LogBook(name=lb_name, uuid=lb_id, backend=self._get_backend()) - fd = flowdetail.FlowDetail('test') - td = taskdetail.TaskDetail("detail-1") + fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid()) + td = taskdetail.TaskDetail("detail-1", uuid=uuidutils.generate_uuid()) fd.add(td) lb.add(fd) lb.save() diff --git a/taskflow/tests/unit/test_functor_task.py b/taskflow/tests/unit/test_functor_task.py index f2f79e9b..72a39aee 100644 --- a/taskflow/tests/unit/test_functor_task.py +++ b/taskflow/tests/unit/test_functor_task.py @@ -16,8 +16,8 @@ # License for the specific language governing permissions and limitations # under the License. -from taskflow import functor_task from taskflow.patterns import linear_flow +from taskflow import task as base from taskflow import test @@ -44,17 +44,17 @@ class BunchOfFunctions(object): class FunctorTaskTest(test.TestCase): def test_simple(self): - task = functor_task.FunctorTask(add) + task = base.FunctorTask(add) self.assertEquals(task.name, __name__ + '.add') def test_other_name(self): - task = functor_task.FunctorTask(add, name='my task') + task = base.FunctorTask(add, name='my task') self.assertEquals(task.name, 'my task') def test_it_runs(self): values = [] bof = BunchOfFunctions(values) - t = functor_task.FunctorTask + t = base.FunctorTask flow = linear_flow.Flow('test') flow.add_many((