diff --git a/doc/source/arguments_and_results.rst b/doc/source/arguments_and_results.rst index 714ee87f..e4f79990 100644 --- a/doc/source/arguments_and_results.rst +++ b/doc/source/arguments_and_results.rst @@ -74,8 +74,8 @@ ignored during inference (as these names have special meaning/usage in python). ... def execute(self, *args, **kwargs): ... pass ... - >>> UniTask().requires - frozenset([]) + >>> sorted(UniTask().requires) + [] .. make vim sphinx highlighter* happy** @@ -214,8 +214,8 @@ name of the value. ... def execute(self): ... return 42 ... - >>> TheAnswerReturningTask(provides='the_answer').provides - frozenset(['the_answer']) + >>> sorted(TheAnswerReturningTask(provides='the_answer').provides) + ['the_answer'] Returning a tuple +++++++++++++++++ diff --git a/taskflow/atom.py b/taskflow/atom.py index 2ade20e6..ebc5bad3 100644 --- a/taskflow/atom.py +++ b/taskflow/atom.py @@ -16,14 +16,28 @@ # under the License. import abc +import collections +import itertools + +try: + from collections import OrderedDict # noqa +except ImportError: + from ordereddict import OrderedDict # noqa from oslo_utils import reflection import six +from six.moves import zip as compat_zip from taskflow import exceptions +from taskflow.types import sets from taskflow.utils import misc +# Helper types tuples... +_sequence_types = (list, tuple, collections.Sequence) +_set_types = (set, collections.Set) + + def _save_as_to_mapping(save_as): """Convert save_as to mapping name => index. @@ -33,25 +47,26 @@ def _save_as_to_mapping(save_as): # outside of code so that it's more easily understandable, since what an # atom returns is pretty crucial for other later operations. if save_as is None: - return {} + return OrderedDict() if isinstance(save_as, six.string_types): # NOTE(harlowja): this means that your atom will only return one item # instead of a dictionary-like object or a indexable object (like a # list or tuple). - return {save_as: None} - elif isinstance(save_as, (tuple, list)): + return OrderedDict([(save_as, None)]) + elif isinstance(save_as, _sequence_types): # NOTE(harlowja): this means that your atom will return a indexable # object, like a list or tuple and the results can be mapped by index # to that tuple/list that is returned for others to use. - return dict((key, num) for num, key in enumerate(save_as)) - elif isinstance(save_as, set): + return OrderedDict((key, num) for num, key in enumerate(save_as)) + elif isinstance(save_as, _set_types): # NOTE(harlowja): in the case where a set is given we will not be - # able to determine the numeric ordering in a reliable way (since it is - # a unordered set) so the only way for us to easily map the result of - # the atom will be via the key itself. - return dict((key, key) for key in save_as) - raise TypeError('Atom provides parameter ' - 'should be str, set or tuple/list, not %r' % save_as) + # able to determine the numeric ordering in a reliable way (since it + # may be an unordered set) so the only way for us to easily map the + # result of the atom will be via the key itself. + return OrderedDict((key, key) for key in save_as) + else: + raise TypeError('Atom provides parameter ' + 'should be str, set or tuple/list, not %r' % save_as) def _build_rebind_dict(args, rebind_args): @@ -62,9 +77,9 @@ def _build_rebind_dict(args, rebind_args): new name onto the required name). """ if rebind_args is None: - return {} + return OrderedDict() elif isinstance(rebind_args, (list, tuple)): - rebind = dict(zip(args, rebind_args)) + rebind = OrderedDict(compat_zip(args, rebind_args)) if len(args) < len(rebind_args): rebind.update((a, a) for a in rebind_args[len(args):]) return rebind @@ -85,11 +100,11 @@ def _build_arg_mapping(atom_name, reqs, rebind_args, function, do_infer, extra arguments (where applicable). """ - # build a list of required arguments based on function signature + # Build a list of required arguments based on function signature. req_args = reflection.get_callable_args(function, required_only=True) all_args = reflection.get_callable_args(function, required_only=False) - # remove arguments that are part of ignore_list + # Remove arguments that are part of ignore list. if ignore_list: for arg in ignore_list: if arg in req_args: @@ -97,39 +112,45 @@ def _build_arg_mapping(atom_name, reqs, rebind_args, function, do_infer, else: ignore_list = [] - required = {} - # add reqs to required mappings + # Build the required names. + required = OrderedDict() + + # Add required arguments to required mappings if inference is enabled. + if do_infer: + required.update((a, a) for a in req_args) + + # Add additional manually provided requirements to required mappings. if reqs: if isinstance(reqs, six.string_types): required.update({reqs: reqs}) else: required.update((a, a) for a in reqs) - # add req_args to required mappings if do_infer is set - if do_infer: - required.update((a, a) for a in req_args) - - # update required mappings based on rebind_args + # Update required mappings values based on rebinding of arguments names. required.update(_build_rebind_dict(req_args, rebind_args)) + # Determine if there are optional arguments that we may or may not take. if do_infer: - opt_args = set(all_args) - set(required) - set(ignore_list) - optional = dict((a, a) for a in opt_args) + opt_args = sets.OrderedSet(all_args) + opt_args = opt_args - set(itertools.chain(six.iterkeys(required), + iter(ignore_list))) + optional = OrderedDict((a, a) for a in opt_args) else: - optional = {} + optional = OrderedDict() + # Check if we are given some extra arguments that we aren't able to accept. if not reflection.accepts_kwargs(function): - extra_args = set(required) - set(all_args) + extra_args = sets.OrderedSet(six.iterkeys(required)) + extra_args -= all_args if extra_args: - extra_args_str = ', '.join(sorted(extra_args)) raise ValueError('Extra arguments given to atom %s: %s' - % (atom_name, extra_args_str)) + % (atom_name, list(extra_args))) # NOTE(imelnikov): don't use set to preserve order in error message missing_args = [arg for arg in req_args if arg not in required] if missing_args: raise ValueError('Missing arguments for atom %s: %s' - % (atom_name, ' ,'.join(missing_args))) + % (atom_name, missing_args)) return required, optional @@ -161,52 +182,53 @@ class Atom(object): with this atom. It can be useful in resuming older versions of atoms. Standard major, minor versioning concepts should apply. - :ivar save_as: An *immutable* output ``resource`` name dictionary this atom - produces that other atoms may depend on this atom providing. - The format is output index (or key when a dictionary - is returned from the execute method) to stored argument - name. - :ivar rebind: An *immutable* input ``resource`` mapping dictionary that - can be used to alter the inputs given to this atom. It is - typically used for mapping a prior atoms output into + :ivar save_as: An *immutable* output ``resource`` name + :py:class:`.OrderedDict` this atom produces that other + atoms may depend on this atom providing. The format is + output index (or key when a dictionary is returned from + the execute method) to stored argument name. + :ivar rebind: An *immutable* input ``resource`` :py:class:`.OrderedDict` + that can be used to alter the inputs given to this atom. It + is typically used for mapping a prior atoms output into the names that this atom expects (in a way this is like remapping a namespace of another atom into the namespace of this atom). :ivar inject: See parameter ``inject``. :ivar name: See parameter ``name``. - :ivar requires: An *immutable* set of inputs this atom requires to - function. - :ivar optional: An *immutable* set of inputs that are optional for this - atom to function. - :ivar provides: An *immutable* set of outputs this atom produces. + :ivar requires: A :py:class:`~taskflow.types.sets.OrderedSet` of inputs + this atom requires to function. + :ivar optional: A :py:class:`~taskflow.types.sets.OrderedSet` of inputs + that are optional for this atom to function. + :ivar provides: A :py:class:`~taskflow.types.sets.OrderedSet` of outputs + this atom produces. """ def __init__(self, name=None, provides=None, inject=None): self.name = name - self.save_as = _save_as_to_mapping(provides) self.version = (1, 0) self.inject = inject - self.requires = frozenset() - self.optional = frozenset() - self.provides = frozenset(self.save_as) - self.rebind = {} + self.save_as = _save_as_to_mapping(provides) + self.requires = sets.OrderedSet() + self.optional = sets.OrderedSet() + self.provides = sets.OrderedSet(self.save_as) + self.rebind = OrderedDict() def _build_arg_mapping(self, executor, requires=None, rebind=None, auto_extract=True, ignore_list=None): - req_arg, opt_arg = _build_arg_mapping(self.name, requires, rebind, - executor, auto_extract, - ignore_list) - self.rebind.clear() - if opt_arg: - self.rebind.update(opt_arg) - if req_arg: - self.rebind.update(req_arg) - self.requires = frozenset(req_arg.values()) - self.optional = frozenset(opt_arg.values()) + required, optional = _build_arg_mapping(self.name, requires, rebind, + executor, auto_extract, + ignore_list=ignore_list) + rebind = OrderedDict() + for (arg_name, bound_name) in itertools.chain(six.iteritems(required), + six.iteritems(optional)): + rebind.setdefault(arg_name, bound_name) + self.rebind = rebind + self.requires = sets.OrderedSet(six.itervalues(required)) + self.optional = sets.OrderedSet(six.itervalues(optional)) if self.inject: - inject_set = set(six.iterkeys(self.inject)) - self.requires -= inject_set - self.optional -= inject_set + inject_keys = frozenset(six.iterkeys(self.inject)) + self.requires -= inject_keys + self.optional -= inject_keys out_of_order = self.provides.intersection(self.requires) if out_of_order: raise exceptions.DependencyFailure( diff --git a/taskflow/engines/action_engine/compiler.py b/taskflow/engines/action_engine/compiler.py index 6116cb01..ff86de27 100644 --- a/taskflow/engines/action_engine/compiler.py +++ b/taskflow/engines/action_engine/compiler.py @@ -157,13 +157,22 @@ class Linker(object): " decomposed into an empty graph" % (v, u, u)) for u in u_g.nodes_iter(): for v in v_g.nodes_iter(): - depends_on = u.provides & v.requires + # This is using the intersection() method vs the & + # operator since the latter doesn't work with frozen + # sets (when used in combination with ordered sets). + # + # If this is not done the following happens... + # + # TypeError: unsupported operand type(s) + # for &: 'frozenset' and 'OrderedSet' + depends_on = u.provides.intersection(v.requires) if depends_on: + edge_attrs = { + _EDGE_REASONS: frozenset(depends_on), + } _add_update_edges(graph, [u], [v], - attr_dict={ - _EDGE_REASONS: depends_on, - }) + attr_dict=edge_attrs) else: # Connect nodes with no predecessors in v to nodes with no # successors in the *first* non-empty predecessor of v (thus diff --git a/taskflow/patterns/graph_flow.py b/taskflow/patterns/graph_flow.py index f71f285b..d36a72ff 100644 --- a/taskflow/patterns/graph_flow.py +++ b/taskflow/patterns/graph_flow.py @@ -27,11 +27,20 @@ def _unsatisfied_requires(node, graph, *additional_provided): if not requires: return requires for provided in additional_provided: - requires = requires - provided + # This is using the difference() method vs the - + # operator since the latter doesn't work with frozen + # or regular sets (when used in combination with ordered + # sets). + # + # If this is not done the following happens... + # + # TypeError: unsupported operand type(s) + # for -: 'set' and 'OrderedSet' + requires = requires.difference(provided) if not requires: return requires for pred in graph.bfs_predecessors_iter(node): - requires = requires - pred.provides + requires = requires.difference(pred.provides) if not requires: return requires return requires diff --git a/taskflow/tests/unit/test_flow_dependencies.py b/taskflow/tests/unit/test_flow_dependencies.py index 69f4a8fe..9627a696 100644 --- a/taskflow/tests/unit/test_flow_dependencies.py +++ b/taskflow/tests/unit/test_flow_dependencies.py @@ -27,40 +27,40 @@ class FlowDependenciesTest(test.TestCase): def test_task_without_dependencies(self): flow = utils.TaskNoRequiresNoReturns() - self.assertEqual(flow.requires, set()) - self.assertEqual(flow.provides, set()) + self.assertEqual(set(), flow.requires) + self.assertEqual(set(), flow.provides) def test_task_requires_default_values(self): flow = utils.TaskMultiArg() - self.assertEqual(flow.requires, set(['x', 'y', 'z'])) - self.assertEqual(flow.provides, set()) + self.assertEqual(set(['x', 'y', 'z']), flow.requires) + self.assertEqual(set(), flow.provides, ) def test_task_requires_rebinded_mapped(self): flow = utils.TaskMultiArg(rebind={'x': 'a', 'y': 'b', 'z': 'c'}) - self.assertEqual(flow.requires, set(['a', 'b', 'c'])) - self.assertEqual(flow.provides, set()) + self.assertEqual(set(['a', 'b', 'c']), flow.requires) + self.assertEqual(set(), flow.provides) def test_task_requires_additional_values(self): flow = utils.TaskMultiArg(requires=['a', 'b']) - self.assertEqual(flow.requires, set(['a', 'b', 'x', 'y', 'z'])) - self.assertEqual(flow.provides, set()) + self.assertEqual(set(['a', 'b', 'x', 'y', 'z']), flow.requires) + self.assertEqual(set(), flow.provides) def test_task_provides_values(self): flow = utils.TaskMultiReturn(provides=['a', 'b', 'c']) - self.assertEqual(flow.requires, set()) - self.assertEqual(flow.provides, set(['a', 'b', 'c'])) + self.assertEqual(set(), flow.requires) + self.assertEqual(set(['a', 'b', 'c']), flow.provides) def test_task_provides_and_requires_values(self): flow = utils.TaskMultiArgMultiReturn(provides=['a', 'b', 'c']) - self.assertEqual(flow.requires, set(['x', 'y', 'z'])) - self.assertEqual(flow.provides, set(['a', 'b', 'c'])) + self.assertEqual(set(['x', 'y', 'z']), flow.requires) + self.assertEqual(set(['a', 'b', 'c']), flow.provides) def test_linear_flow_without_dependencies(self): flow = lf.Flow('lf').add( utils.TaskNoRequiresNoReturns('task1'), utils.TaskNoRequiresNoReturns('task2')) - self.assertEqual(flow.requires, set()) - self.assertEqual(flow.provides, set()) + self.assertEqual(set(), flow.requires) + self.assertEqual(set(), flow.provides) def test_linear_flow_requires_values(self): flow = lf.Flow('lf').add( diff --git a/taskflow/tests/unit/test_task.py b/taskflow/tests/unit/test_task.py index 9a9ae1c9..72f98863 100644 --- a/taskflow/tests/unit/test_task.py +++ b/taskflow/tests/unit/test_task.py @@ -52,36 +52,36 @@ class TaskTest(test.TestCase): def test_passed_name(self): my_task = MyTask(name='my name') - self.assertEqual(my_task.name, 'my name') + self.assertEqual('my name', my_task.name) def test_generated_name(self): my_task = MyTask() - self.assertEqual(my_task.name, - '%s.%s' % (__name__, 'MyTask')) + self.assertEqual('%s.%s' % (__name__, 'MyTask'), + my_task.name) def test_task_str(self): my_task = MyTask(name='my') - self.assertEqual(str(my_task), 'my==1.0') + self.assertEqual('my==1.0', str(my_task)) def test_task_repr(self): my_task = MyTask(name='my') - self.assertEqual(repr(my_task), '<%s.MyTask my==1.0>' % __name__) + self.assertEqual('<%s.MyTask my==1.0>' % __name__, repr(my_task)) def test_no_provides(self): my_task = MyTask() - self.assertEqual(my_task.save_as, {}) + self.assertEqual({}, my_task.save_as) def test_provides(self): my_task = MyTask(provides='food') - self.assertEqual(my_task.save_as, {'food': None}) + self.assertEqual({'food': None}, my_task.save_as) def test_multi_provides(self): my_task = MyTask(provides=('food', 'water')) - self.assertEqual(my_task.save_as, {'food': 0, 'water': 1}) + self.assertEqual({'food': 0, 'water': 1}, my_task.save_as) def test_unpack(self): my_task = MyTask(provides=('food',)) - self.assertEqual(my_task.save_as, {'food': 0}) + self.assertEqual({'food': 0}, my_task.save_as) def test_bad_provides(self): self.assertRaisesRegexp(TypeError, '^Atom provides', @@ -89,28 +89,34 @@ class TaskTest(test.TestCase): def test_requires_by_default(self): my_task = MyTask() - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'spam', 'eggs': 'eggs', 'context': 'context' - }) + } + self.assertEqual(expected, + my_task.rebind) + self.assertEqual(set(['spam', 'eggs', 'context']), + my_task.requires) def test_requires_amended(self): my_task = MyTask(requires=('spam', 'eggs')) - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'spam', 'eggs': 'eggs', 'context': 'context' - }) + } + self.assertEqual(expected, my_task.rebind) def test_requires_explicit(self): my_task = MyTask(auto_extract=False, requires=('spam', 'eggs', 'context')) - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'spam', 'eggs': 'eggs', 'context': 'context' - }) + } + self.assertEqual(expected, my_task.rebind) def test_requires_explicit_not_enough(self): self.assertRaisesRegexp(ValueError, '^Missing arguments', @@ -119,36 +125,43 @@ class TaskTest(test.TestCase): def test_requires_ignores_optional(self): my_task = DefaultArgTask() - self.assertEqual(my_task.requires, set(['spam'])) - self.assertEqual(my_task.optional, set(['eggs'])) + self.assertEqual(set(['spam']), my_task.requires) + self.assertEqual(set(['eggs']), my_task.optional) def test_requires_allows_optional(self): my_task = DefaultArgTask(requires=('spam', 'eggs')) - self.assertEqual(my_task.requires, set(['spam', 'eggs'])) - self.assertEqual(my_task.optional, set()) + self.assertEqual(set(['spam', 'eggs']), my_task.requires) + self.assertEqual(set(), my_task.optional) def test_rebind_includes_optional(self): my_task = DefaultArgTask() - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'spam', 'eggs': 'eggs', - }) + } + self.assertEqual(expected, my_task.rebind) def test_rebind_all_args(self): my_task = MyTask(rebind={'spam': 'a', 'eggs': 'b', 'context': 'c'}) - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'a', 'eggs': 'b', 'context': 'c' - }) + } + self.assertEqual(expected, my_task.rebind) + self.assertEqual(set(['a', 'b', 'c']), + my_task.requires) def test_rebind_partial(self): my_task = MyTask(rebind={'spam': 'a', 'eggs': 'b'}) - self.assertEqual(my_task.rebind, { + expected = { 'spam': 'a', 'eggs': 'b', 'context': 'context' - }) + } + self.assertEqual(expected, my_task.rebind) + self.assertEqual(set(['a', 'b', 'context']), + my_task.requires) def test_rebind_unknown(self): self.assertRaisesRegexp(ValueError, '^Extra arguments', @@ -156,26 +169,33 @@ class TaskTest(test.TestCase): def test_rebind_unknown_kwargs(self): task = KwargsTask(rebind={'foo': 'bar'}) - self.assertEqual(task.rebind, { + expected = { 'foo': 'bar', 'spam': 'spam' - }) + } + self.assertEqual(expected, task.rebind) def test_rebind_list_all(self): my_task = MyTask(rebind=('a', 'b', 'c')) - self.assertEqual(my_task.rebind, { + expected = { 'context': 'a', 'spam': 'b', 'eggs': 'c' - }) + } + self.assertEqual(expected, my_task.rebind) + self.assertEqual(set(['a', 'b', 'c']), + my_task.requires) def test_rebind_list_partial(self): my_task = MyTask(rebind=('a', 'b')) - self.assertEqual(my_task.rebind, { + expected = { 'context': 'a', 'spam': 'b', 'eggs': 'eggs' - }) + } + self.assertEqual(expected, my_task.rebind) + self.assertEqual(set(['a', 'b', 'eggs']), + my_task.requires) def test_rebind_list_more(self): self.assertRaisesRegexp(ValueError, '^Extra arguments', @@ -183,11 +203,14 @@ class TaskTest(test.TestCase): def test_rebind_list_more_kwargs(self): task = KwargsTask(rebind=('a', 'b', 'c')) - self.assertEqual(task.rebind, { + expected = { 'spam': 'a', 'b': 'b', 'c': 'c' - }) + } + self.assertEqual(expected, task.rebind) + self.assertEqual(set(['a', 'b', 'c']), + task.requires) def test_rebind_list_bad_value(self): self.assertRaisesRegexp(TypeError, '^Invalid rebind value', @@ -195,13 +218,13 @@ class TaskTest(test.TestCase): def test_default_provides(self): task = DefaultProvidesTask() - self.assertEqual(task.provides, set(['def'])) - self.assertEqual(task.save_as, {'def': None}) + self.assertEqual(set(['def']), task.provides) + self.assertEqual({'def': None}, task.save_as) def test_default_provides_can_be_overridden(self): task = DefaultProvidesTask(provides=('spam', 'eggs')) - self.assertEqual(task.provides, set(['spam', 'eggs'])) - self.assertEqual(task.save_as, {'spam': 0, 'eggs': 1}) + self.assertEqual(set(['spam', 'eggs']), task.provides) + self.assertEqual({'spam': 0, 'eggs': 1}, task.save_as) def test_update_progress_within_bounds(self): values = [0.0, 0.5, 1.0] @@ -213,7 +236,7 @@ class TaskTest(test.TestCase): a_task = ProgressTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, progress_callback) a_task.execute(values) - self.assertEqual(result, values) + self.assertEqual(values, result) @mock.patch.object(task.LOG, 'warn') def test_update_progress_lower_bound(self, mocked_warn): @@ -225,8 +248,8 @@ class TaskTest(test.TestCase): a_task = ProgressTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, progress_callback) a_task.execute([-1.0, -0.5, 0.0]) - self.assertEqual(result, [0.0, 0.0, 0.0]) - self.assertEqual(mocked_warn.call_count, 2) + self.assertEqual([0.0, 0.0, 0.0], result) + self.assertEqual(2, mocked_warn.call_count) @mock.patch.object(task.LOG, 'warn') def test_update_progress_upper_bound(self, mocked_warn): @@ -238,8 +261,8 @@ class TaskTest(test.TestCase): a_task = ProgressTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, progress_callback) a_task.execute([1.0, 1.5, 2.0]) - self.assertEqual(result, [1.0, 1.0, 1.0]) - self.assertEqual(mocked_warn.call_count, 2) + self.assertEqual([1.0, 1.0, 1.0], result) + self.assertEqual(2, mocked_warn.call_count) @mock.patch.object(notifier.LOG, 'warn') def test_update_progress_handler_failure(self, mocked_warn): @@ -256,34 +279,34 @@ class TaskTest(test.TestCase): a_task = MyTask() self.assertRaises(ValueError, a_task.notifier.register, task.EVENT_UPDATE_PROGRESS, None) - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(0, len(a_task.notifier)) def test_deregister_any_handler(self): a_task = MyTask() - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(0, len(a_task.notifier)) a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, lambda event_type, details: None) - self.assertEqual(len(a_task.notifier), 1) + self.assertEqual(1, len(a_task.notifier)) a_task.notifier.deregister_event(task.EVENT_UPDATE_PROGRESS) - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(0, len(a_task.notifier)) def test_deregister_any_handler_empty_listeners(self): a_task = MyTask() - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(0, len(a_task.notifier)) self.assertFalse(a_task.notifier.deregister_event( task.EVENT_UPDATE_PROGRESS)) - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(0, len(a_task.notifier)) def test_deregister_non_existent_listener(self): handler1 = lambda event_type, details: None handler2 = lambda event_type, details: None a_task = MyTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, handler1) - self.assertEqual(len(list(a_task.notifier.listeners_iter())), 1) + self.assertEqual(1, len(list(a_task.notifier.listeners_iter()))) a_task.notifier.deregister(task.EVENT_UPDATE_PROGRESS, handler2) - self.assertEqual(len(list(a_task.notifier.listeners_iter())), 1) + self.assertEqual(1, len(list(a_task.notifier.listeners_iter()))) a_task.notifier.deregister(task.EVENT_UPDATE_PROGRESS, handler1) - self.assertEqual(len(list(a_task.notifier.listeners_iter())), 0) + self.assertEqual(0, len(list(a_task.notifier.listeners_iter()))) def test_bind_not_callable(self): a_task = MyTask() @@ -295,8 +318,8 @@ class TaskTest(test.TestCase): a_task = MyTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, handler1) b_task = a_task.copy(retain_listeners=False) - self.assertEqual(len(a_task.notifier), 1) - self.assertEqual(len(b_task.notifier), 0) + self.assertEqual(1, len(a_task.notifier)) + self.assertEqual(0, len(b_task.notifier)) def test_copy_listeners(self): handler1 = lambda event_type, details: None @@ -304,15 +327,15 @@ class TaskTest(test.TestCase): a_task = MyTask() a_task.notifier.register(task.EVENT_UPDATE_PROGRESS, handler1) b_task = a_task.copy() - self.assertEqual(len(b_task.notifier), 1) + self.assertEqual(1, len(b_task.notifier)) self.assertTrue(a_task.notifier.deregister_event( task.EVENT_UPDATE_PROGRESS)) - self.assertEqual(len(a_task.notifier), 0) - self.assertEqual(len(b_task.notifier), 1) + self.assertEqual(0, len(a_task.notifier)) + self.assertEqual(1, len(b_task.notifier)) b_task.notifier.register(task.EVENT_UPDATE_PROGRESS, handler2) listeners = dict(list(b_task.notifier.listeners_iter())) - self.assertEqual(len(listeners[task.EVENT_UPDATE_PROGRESS]), 2) - self.assertEqual(len(a_task.notifier), 0) + self.assertEqual(2, len(listeners[task.EVENT_UPDATE_PROGRESS])) + self.assertEqual(0, len(a_task.notifier)) class FunctorTaskTest(test.TestCase): @@ -320,7 +343,7 @@ class FunctorTaskTest(test.TestCase): def test_creation_with_version(self): version = (2, 0) f_task = task.FunctorTask(lambda: None, version=version) - self.assertEqual(f_task.version, version) + self.assertEqual(version, f_task.version) def test_execute_not_callable(self): self.assertRaises(ValueError, task.FunctorTask, 2)