def _call_objs(self, contextualized): # Setup pre_body, post_body = Mock(), Mock() t1 = Task(pre_body, contextualized=contextualized) t2 = Task(post_body, contextualized=contextualized) t3 = Task( Mock(), pre=[call(t1, 5, foo='bar')], post=[call(t2, 7, biz='baz')], ) c = Collection(t1=t1, t2=t2, t3=t3) e = Executor(collection=c) e.execute('t3') # Pre-task asserts args, kwargs = pre_body.call_args eq_(kwargs, {'foo': 'bar'}) if contextualized: assert isinstance(args[0], Context) eq_(args[1], 5) else: eq_(args, (5, )) # Post-task asserts args, kwargs = post_body.call_args eq_(kwargs, {'biz': 'baz'}) if contextualized: assert isinstance(args[0], Context) eq_(args[1], 7) else: eq_(args, (7, ))
def calls_default_to_empty_args_always(self): pre_body, post_body = Mock(), Mock() t1 = Task(pre_body) t2 = Task(post_body) t3 = Task(Mock(), pre=[t1], post=[t2]) e = Executor(collection=Collection(t1=t1, t2=t2, t3=t3)) e.execute(('t3', {'something': 'meh'})) for body in (pre_body, post_body): eq_(body.call_args, tuple())
def deduping_treats_different_calls_to_same_task_differently(self): body = Mock() t1 = Task(body) pre = [call(t1, 5), call(t1, 7), call(t1, 5)] t2 = Task(Mock(), pre=pre) c = Collection(t1=t1, t2=t2) e = Executor(collection=c) e.execute('t2') # Does not call the second t1(5) body.assert_has_calls([mock_call(5), mock_call(7)])
def calls_default_to_empty_args_always(self): pre_body, post_body = Mock(), Mock() t1 = Task(pre_body) t2 = Task(post_body) t3 = Task(Mock(), pre=[t1], post=[t2]) e = Executor(collection=Collection(t1=t1, t2=t2, t3=t3)) e.execute(('t3', {'something': 'meh'})) for body in (pre_body, post_body): args = body.call_args[0] assert len(args) == 1 assert isinstance(args[0], Context)
def calls_default_to_empty_args_always(self): dep_body, followup_body = Mock(), Mock() t1 = Task(dep_body) t2 = Task(followup_body) t3 = Task(Mock(), depends_on=[t1], afterwards=[t2]) e = Executor(collection=Collection(t1=t1, t2=t2, t3=t3)) e.execute(('t3', {'something': 'meh'})) for body in (dep_body, followup_body): args = body.call_args[0] eq_(len(args), 1) ok_(isinstance(args[0], Context))
def setup(self): self.task1 = Task(Mock(return_value=7)) self.task2 = Task(Mock(return_value=10), pre=[self.task1]) self.task3 = Task(Mock(), pre=[self.task1]) self.task4 = Task(Mock(return_value=15), post=[self.task1]) self.contextualized = Task(Mock()) coll = Collection() coll.add_task(self.task1, name='task1') coll.add_task(self.task2, name='task2') coll.add_task(self.task3, name='task3') coll.add_task(self.task4, name='task4') coll.add_task(self.contextualized, name='contextualized') self.executor = Executor(collection=coll)
def deduping_treats_different_calls_to_same_task_differently(self): body = Mock() t1 = Task(body) pre = [call(t1, 5), call(t1, 7), call(t1, 5)] t2 = Task(Mock(), pre=pre) c = Collection(t1=t1, t2=t2) e = Executor(collection=c) e.execute('t2') # Does not call the second t1(5) param_list = [] for body_call in body.call_args_list: assert isinstance(body_call[0][0], Context) param_list.append(body_call[0][1]) assert set(param_list) == {5, 7}
def graph_treats_different_calls_to_same_task_differently(self): body = Mock() t1 = Task(body) dep = [call(t1, 5), call(t1, 7), call(t1, 5)] t2 = Task(Mock(), depends_on=dep) c = Collection(t1=t1, t2=t2) e = Executor(collection=c) e.execute('t2') # Does not call the second t1(5) param_list = [] for body_call in body.call_args_list: ok_(isinstance(body_call[0][0], Context)) param_list.append(body_call[0][1]) ok_(set(param_list) == set((5, 7)))
def setup(self): s = super(Executor_, self) s.setup() self.task1 = Task(Mock(return_value=7)) self.task2 = Task(Mock(return_value=10), depends_on=[self.task1]) self.task3 = Task(Mock(), requires=[self.task1]) self.task4 = Task(Mock(return_value=15), afterwards=[self.task1]) self.contextualized = Task(Mock()) coll = Collection() coll.add_task(self.task1, name='task1') coll.add_task(self.task2, name='task2') coll.add_task(self.task3, name='task3') coll.add_task(self.task4, name='task4') coll.add_task(self.contextualized, name='contextualized') self.executor = Executor(collection=coll)
def non_null_namespace_does_not_trigger_task_related_args(self): for arg in Program().task_args(): expect( "--help", out=arg.name, program=Program(namespace=Collection(mytask=Task(Mock()))), test=assert_not_contains, )
def default_tasks_called_when_no_tasks_specified(self): # NOTE: when no tasks AND no default, Program will print global # help. We just won't do anything at all, which is fine for now. task = Task(Mock('default-task')) coll = Collection() coll.add_task(task, name='mytask', default=True) executor = Executor(collection=coll) executor.execute() task.body.assert_called_with()
def bundled_namespace_help_includes_subcommand_listing(self): t1, t2 = Task(Mock()), Task(Mock()) coll = Collection(task1=t1, task2=t2) p = Program(namespace=coll) # Spot checks for expected bits, so we don't have to change # this every time core args change. for expected in ( # Usage line changes somewhat "Usage: myapp [--core-opts] <subcommand> [--subcommand-opts] ...\n", # noqa # Core options are still present "Core options:\n", "--echo", # Subcommands are listed "Subcommands:\n", " task1", " task2", ): stdout, _ = run("myapp --help", program=p, invoke=False) assert expected in stdout
def default_tasks_called_when_no_tasks_specified(self): # NOTE: when no tasks AND no default, Program will print global # help. We just won't do anything at all, which is fine for now. task = Task(Mock('default-task')) coll = Collection() coll.add_task(task, name='mytask', default=True) executor = Executor(collection=coll) executor.execute() args = task.body.call_args[0] assert isinstance(args[0], Context) assert len(args) == 1
def expand_calls(self, calls, apply_hosts=True): # Generate new call list with per-host variants & Connections inserted ret = [] cli_hosts = [] host_str = self.core[0].args.hosts.value if apply_hosts and host_str: cli_hosts = host_str.split(",") config_hosts = self.config.hosts for call in calls: if isinstance(call, Task): call = Call(task=call) # TODO: expand this to allow multiple types of execution plans, # pending outcome of invoke#461 (which, if flexible enough to # handle intersect of dependencies+parameterization, just becomes # 'honor that new feature of Invoke') # TODO: roles, other non-runtime host parameterizations, etc # Pre-tasks get added only once, not once per host. ret.extend(self.expand_calls(call.pre, apply_hosts=False)) # Determine final desired host list based on CLI and task values # (with CLI, being closer to runtime, winning) and normalize to # Connection-init kwargs. call_hosts = getattr(call, "hosts", None) cxn_params = self.normalize_hosts(cli_hosts or config_hosts or call_hosts) # Main task, per host/connection for init_kwargs in cxn_params: ret.append(self.parameterize(call, init_kwargs)) # Deal with lack of hosts list (acts same as `inv` in that case) # TODO: no tests for this branch? if not cxn_params: ret.append(call) # Post-tasks added once, not once per host. ret.extend(self.expand_calls(call.post, apply_hosts=False)) # Add remainder as anonymous task if self.core.remainder: # TODO: this will need to change once there are more options for # setting host lists besides "-H or 100% within-task" if not cli_hosts: raise NothingToDo( "Was told to run a command, but not given any hosts to run it on!" # noqa ) def anonymous(c): c.run(self.core.remainder) anon = Call(Task(body=anonymous)) # TODO: see above TODOs about non-parameterized setups, roles etc # TODO: will likely need to refactor that logic some more so it can # be used both there and here. for init_kwargs in self.normalize_hosts(cli_hosts): ret.append(self.parameterize(anon, init_kwargs)) return ret
def call_objs_play_well_with_context_args(self): # Setup dep_body, followup_body = Mock(), Mock() t1 = Task(dep_body) t2 = Task(followup_body) t3 = Task(Mock(), depends_on=[call(t1, 5, foo='bar')], afterwards=[call(t2, 7, biz='baz')], ) c = Collection(t1=t1, t2=t2, t3=t3) e = Executor(collection=c) e.execute('t3') # Dependency asserts args, kwargs = dep_body.call_args eq_(kwargs, {'foo': 'bar'}) assert isinstance(args[0], Context) eq_(args[1], 5) # Followup asserts args, kwargs = followup_body.call_args eq_(kwargs, {'biz': 'baz'}) assert isinstance(args[0], Context) eq_(args[1], 7)
def _call_objs(self): # Setup pre_body, post_body = Mock(), Mock() t1 = Task(pre_body) t2 = Task(post_body) t3 = Task( Mock(), pre=[call(t1, 5, foo='bar')], post=[call(t2, 7, biz='baz')], ) c = Collection(t1=t1, t2=t2, t3=t3) e = Executor(collection=c) e.execute('t3') # Pre-task asserts args, kwargs = pre_body.call_args assert kwargs == {'foo': 'bar'} assert isinstance(args[0], Context) assert args[1] == 5 # Post-task asserts args, kwargs = post_body.call_args assert kwargs == {'biz': 'baz'} assert isinstance(args[0], Context) assert args[1] == 7
def _call_objs(self): # Setup pre_body, post_body = Mock(), Mock() t1 = Task(pre_body) t2 = Task(post_body) t3 = Task( Mock(), pre=[call(t1, 5, foo="bar")], post=[call(t2, 7, biz="baz")], ) c = Collection(t1=t1, t2=t2, t3=t3) e = Executor(collection=c) e.execute("t3") # Pre-task asserts args, kwargs = pre_body.call_args assert kwargs == {"foo": "bar"} assert isinstance(args[0], Context) assert args[1] == 5 # Post-task asserts args, kwargs = post_body.call_args assert kwargs == {"biz": "baz"} assert isinstance(args[0], Context) assert args[1] == 7
def expand_calls(self, calls, apply_hosts=True): # Generate new call list with per-host variants & Connections inserted ret = [] # TODO: mesh well with Invoke list-type args helper (inv #132) hosts = [] host_str = self.core[0].args.hosts.value if apply_hosts and host_str: hosts = host_str.split(",") for call in calls: if isinstance(call, Task): call = Call(task=call) # TODO: expand this to allow multiple types of execution plans, # pending outcome of invoke#461 (which, if flexible enough to # handle intersect of dependencies+parameterization, just becomes # 'honor that new feature of Invoke') # TODO: roles, other non-runtime host parameterizations, etc # Pre-tasks get added only once, not once per host. ret.extend(self.expand_calls(call.pre, apply_hosts=False)) # Main task, per host for host in hosts: ret.append(self.parameterize(call, host)) # Deal with lack of hosts arg (acts same as `inv` in that case) # TODO: no tests for this branch? if not hosts: ret.append(call) # Post-tasks added once, not once per host. ret.extend(self.expand_calls(call.post, apply_hosts=False)) # Add remainder as anonymous task if self.core.remainder: # TODO: this will need to change once there are more options for # setting host lists besides "-H or 100% within-task" if not hosts: raise NothingToDo( "Was told to run a command, but not given any hosts to run it on!" # noqa ) def anonymous(c): # TODO: how to make all our tests configure in_stream=False? c.run(self.core.remainder, in_stream=False) anon = Call(Task(body=anonymous)) # TODO: see above TODOs about non-parameterized setups, roles etc # TODO: will likely need to refactor that logic some more so it can # be used both there and here. for host in hosts: ret.append(self.parameterize(anon, host)) return ret
def name_defaults_to_body_name(self): assert Task(_func).name == '_func'
def non_null_namespace_does_not_trigger_task_related_args(self): for arg in Program().task_args(): program = Program(namespace=Collection(mytask=Task(Mock()))) stdout, _ = run("--help", program=program) assert arg.name not in stdout
def setup(self): self.task = Task(Mock(__name__='mytask'))
def can_override_name(self): eq_(Task(_func, name='foo').name, 'foo')
def name_defaults_to_body_name(self): eq_(Task(_func).name, '_func')
def has_default_flag(self): eq_(Task(_func).is_default, False)
def equality_testing(self): t1 = Task(_func, name='foo') t2 = Task(_func, name='foo') eq_(t1, t2) t3 = Task(_func, name='bar') assert t1 != t3
def has_useful_repr(self): i = repr(Task(_func)) assert '_func' in i, "'func' not found in {0!r}".format(i) e = repr(Task(_func, name='funky')) assert 'funky' in e, "'funky' not found in {0!r}".format(e) assert '_func' not in e, "'_func' unexpectedly seen in {0!r}".format(e)
def can_override_name(self): assert Task(_func, name="foo").name == "foo"
def setup(self): self.task = Task(Mock(__name__="mytask"))
def can_override_name(self): assert Task(_func, name='foo').name == 'foo'
def has_default_flag(self): assert Task(_func).is_default is False