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
This commit is contained in:
Joshua Harlow
2013-08-27 16:58:10 +04:00
parent 7a09c044e3
commit 0fa642d949
7 changed files with 92 additions and 137 deletions

View File

@@ -18,7 +18,7 @@
import functools import functools
from taskflow import functor_task from taskflow import task as base
from taskflow import utils from taskflow import utils
@@ -78,7 +78,7 @@ def task(*args, **kwargs):
# NOTE(imelnikov): we can't capture f here because for # NOTE(imelnikov): we can't capture f here because for
# bound methods and bound class methods the object it # bound methods and bound class methods the object it
# is bound to is yet unknown at the moment # 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) w_f = _original_function(f)
setattr(w_f, utils.TASK_FACTORY_ATTRIBUTE, task_factory) setattr(w_f, utils.TASK_FACTORY_ATTRIBUTE, task_factory)
return f return f

View File

@@ -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

View File

@@ -16,7 +16,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from taskflow.openstack.common import uuidutils
from taskflow.persistence.backends import api as b_api 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 persisted when the logbook that contains this flow detail is saved or when
the save() method is called directly. the save() method is called directly.
""" """
def __init__(self, name, uuid=None, backend='memory'): def __init__(self, name, uuid, backend='memory'):
if uuid: self._uuid = uuid
self._uuid = uuid
else:
self._uuid = uuidutils.generate_uuid()
self._name = name self._name = name
self._taskdetails = [] self._taskdetails = []
self.state = None self.state = None

View File

@@ -16,7 +16,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from taskflow.openstack.common import uuidutils
from taskflow.persistence.backends import api as b_api 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 persisted when the logbook that contains this task detail is saved or when
the save() method is called directly. the save() method is called directly.
""" """
def __init__(self, name, task=None, uuid=None, backend='memory'): def __init__(self, name, uuid, backend='memory'):
if uuid: self._uuid = uuid
self._uuid = uuid
else:
self._uuid = uuidutils.generate_uuid()
self._name = name self._name = name
self.backend = backend
# TODO(harlowja): decide if these should be passed in and therefore # TODO(harlowja): decide if these should be passed in and therefore
# immutable or let them be assigned? # immutable or let them be assigned?
# #
@@ -60,12 +57,6 @@ class TaskDetail(object):
# is quite useful for determining what versions of tasks this detail # is quite useful for determining what versions of tasks this detail
# information can be associated with. # information can be associated with.
self.version = None 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): def save(self):
"""Saves *most* of the components of this given object. """Saves *most* of the components of this given object.

View File

@@ -17,10 +17,14 @@
# under the License. # under the License.
import abc import abc
import inspect
from taskflow.openstack.common import uuidutils
from taskflow import utils 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): class Task(object):
"""An abstraction that defines a potential piece of work that can be """An abstraction that defines a potential piece of work that can be
@@ -28,11 +32,7 @@ class Task(object):
""" """
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
def __init__(self, name, task_id=None): def __init__(self, name):
if task_id:
self._uuid = task_id
else:
self._uuid = uuidutils.generate_uuid()
self._name = name self._name = name
# An *immutable* input 'resource' name set this task depends # An *immutable* input 'resource' name set this task depends
# on existing before this task can be applied. # on existing before this task can be applied.
@@ -49,10 +49,6 @@ class Task(object):
# major, minor version semantics apply. # major, minor version semantics apply.
self.version = (1, 0) self.version = (1, 0)
@property
def uuid(self):
return self._uuid
@property @property
def name(self): def name(self):
return self._name return self._name
@@ -75,3 +71,70 @@ class Task(object):
provided as well as any information which may have caused provided as well as any information which may have caused
said reversion. 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

View File

@@ -56,7 +56,7 @@ class PersistenceTestMixin(object):
lb = logbook.LogBook(name=lb_name, uuid=lb_id, lb = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test') fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid())
lb.add(fd) lb.add(fd)
# Ensure we can't save it since its owning logbook hasn't been # 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, lb = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test') fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid())
lb.add(fd) lb.add(fd)
td = taskdetail.TaskDetail("detail-1") td = taskdetail.TaskDetail("detail-1", uuid=uuidutils.generate_uuid())
fd.add(td) fd.add(td)
# Ensure we can't save it since its owning logbook hasn't been # 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, lb = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test') fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid())
lb.add(fd) lb.add(fd)
lb.save() lb.save()
lb2 = logbook.LogBook(name=lb_name, uuid=lb_id, lb2 = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test2') fd = flowdetail.FlowDetail('test2', uuid=uuidutils.generate_uuid())
lb2.add(fd) lb2.add(fd)
lb2.save() lb2.save()
@@ -113,7 +113,7 @@ class PersistenceTestMixin(object):
lb = logbook.LogBook(name=lb_name, uuid=lb_id, lb = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test') fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid())
lb.add(fd) lb.add(fd)
lb.save() lb.save()
@@ -129,8 +129,8 @@ class PersistenceTestMixin(object):
lb = logbook.LogBook(name=lb_name, uuid=lb_id, lb = logbook.LogBook(name=lb_name, uuid=lb_id,
backend=self._get_backend()) backend=self._get_backend())
fd = flowdetail.FlowDetail('test') fd = flowdetail.FlowDetail('test', uuid=uuidutils.generate_uuid())
td = taskdetail.TaskDetail("detail-1") td = taskdetail.TaskDetail("detail-1", uuid=uuidutils.generate_uuid())
fd.add(td) fd.add(td)
lb.add(fd) lb.add(fd)
lb.save() lb.save()

View File

@@ -16,8 +16,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from taskflow import functor_task
from taskflow.patterns import linear_flow from taskflow.patterns import linear_flow
from taskflow import task as base
from taskflow import test from taskflow import test
@@ -44,17 +44,17 @@ class BunchOfFunctions(object):
class FunctorTaskTest(test.TestCase): class FunctorTaskTest(test.TestCase):
def test_simple(self): def test_simple(self):
task = functor_task.FunctorTask(add) task = base.FunctorTask(add)
self.assertEquals(task.name, __name__ + '.add') self.assertEquals(task.name, __name__ + '.add')
def test_other_name(self): 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') self.assertEquals(task.name, 'my task')
def test_it_runs(self): def test_it_runs(self):
values = [] values = []
bof = BunchOfFunctions(values) bof = BunchOfFunctions(values)
t = functor_task.FunctorTask t = base.FunctorTask
flow = linear_flow.Flow('test') flow = linear_flow.Flow('test')
flow.add_many(( flow.add_many((