def shows_UnexpectedExit_repr_when_streams_hidden(self, mock_exit): p = Program() oops = UnexpectedExit(Result( command='meh', exited=54, stdout='things!', stderr='ohnoz!', hide=('stdout', 'stderr'), )) p.execute = Mock(side_effect=oops) p.run("myapp foo") # Expect repr() of exception prints to stderr # NOTE: this partially duplicates a test in runners.py; whatever. eq_(sys.stderr.getvalue(), """Encountered a bad command exit code! Command: 'meh' Exit code: 54 Stdout: things! Stderr: ohnoz! """) # And exit with expected code (vs e.g. 1 or 0) mock_exit.assert_called_with(54)
def UnexpectedExit_str_encodes_stdout_and_err(self, mock_exit): p = Program() oops = UnexpectedExit(Result( command='meh', exited=54, stdout=u'this is not ascii: \u1234', stderr=u'this is also not ascii: \u4321', encoding='utf-8', hide=('stdout', 'stderr'), )) p.execute = Mock(side_effect=oops) p.run("myapp foo") # NOTE: using explicit binary ASCII here, & accessing raw # getvalue() of the faked sys.stderr (spec.trap auto-decodes it # normally) to have a not-quite-tautological test. otherwise we'd # just be comparing unicode to unicode. shrug? expected = b"""Encountered a bad command exit code! Command: 'meh' Exit code: 54 Stdout: this is not ascii: \xe1\x88\xb4 Stderr: this is also not ascii: \xe4\x8c\xa1 """ got = six.BytesIO.getvalue(sys.stderr) assert got == expected
def defaults_to_sys_argv(self, mock_sys): argv = ['inv', '--version'] mock_sys.argv = argv p = Program() p.print_version = Mock() p.run(exit=False) p.print_version.assert_called()
def expect(invocation, out=None, err=None, program=None, invoke=True, test=None): """ Run ``invocation`` via ``program`` and expect resulting output to match. May give one or both of ``out``/``err`` (but not neither). ``program`` defaults to ``Program()``. To skip automatically assuming the argv under test starts with ``"invoke "``, say ``invoke=False``. To customize the operator used for testing (default: equality), use ``test`` (which should be an assertion wrapper of some kind). """ if program is None: program = Program() if invoke: invocation = "invoke {0}".format(invocation) program.run(invocation, exit=False) # Perform tests if out is not None: (test or eq_)(sys.stdout.getvalue(), out) if err is not None: (test or eq_)(sys.stderr.getvalue(), err)
def null_namespace_triggers_task_related_args(self): program = Program(namespace=None) for arg in program.task_args(): expect( "--help", program=program, out=arg.name, test=assert_contains )
def config_attribute_is_memoized(self): klass = Mock() # Can't .config without .run (meh); .run calls .config once. p = Program(config_class=klass) p.run("myapp foo", exit=False) eq_(klass.call_count, 1) # Second access should use cached value p.config eq_(klass.call_count, 1)
def expected_failure_types_dont_raise_exceptions(self, mock_exit): "expected failure types don't raise exceptions" for side_effect in ( SimpleFailure, ParseError("boo!"), ): p = Program() p.execute = Mock(side_effect=side_effect) p.run("myapp -c foo mytask") # valid task name for parse step # Make sure we still exited fail-wise mock_exit.assert_called_with(1)
def ParseErrors_display_message_and_exit_1(self, mock_exit): p = Program() # Run with a definitely-parser-angering incorrect input; the fact # that this line doesn't raise an exception and thus fail the # test, is what we're testing... nah = 'nopenotvalidsorry' p.run("myapp {0}".format(nah)) # Expect that we did print the core body of the ParseError (e.g. # "no idea what foo is!") and exit 1. (Intent is to display that # info w/o a full traceback, basically.) eq_(sys.stderr.getvalue(), "No idea what '{0}' is!\n".format(nah)) mock_exit.assert_called_with(1)
def UnexpectedExit_exits_with_code_when_no_hiding(self, mock_exit): p = Program() oops = UnexpectedExit( Result(command="meh", exited=17, hide=tuple()) ) p.execute = Mock(side_effect=oops) p.run("myapp foo") # Expect NO repr printed, because stdout/err were not hidden, so we # don't want to add extra annoying verbosity - we want to be more # Make-like here. assert sys.stderr.getvalue() == "" # But we still exit with expected code (vs e.g. 1 or 0) mock_exit.assert_called_with(17)
def env_var_prefix_can_be_overridden(self, monkeypatch): monkeypatch.setenv('MYAPP_RUN_HIDE', 'both') # This forces the execution stuff, including Executor, to run # NOTE: it's not really possible to rework the impl so this test is # cleaner - tasks require per-task/per-collection config, which can # only be realized at the time a given task is to be executed. # Unless we overhaul the Program/Executor relationship so Program # does more of the heavy lifting re: task lookup/load/etc... # NOTE: check-hide will kaboom if its context's run.hide is not set # to True (default False). class MyConf(Config): env_prefix = 'MYAPP' p = Program(config_class=MyConf) p.run('inv -c contextualized check-hide')
def run(invocation, program=None, invoke=True): """ Run ``invocation`` via ``program``, returning output stream captures. ``program`` defaults to ``Program()``. To skip automatically assuming the argv under test starts with ``"invoke "``, say ``invoke=False``. :returns: Two-tuple of ``stdout, stderr`` strings. """ if program is None: program = Program() if invoke: invocation = "invoke {}".format(invocation) program.run(invocation, exit=False) return sys.stdout.getvalue(), sys.stderr.getvalue()
def default_version_is_unknown(self): eq_(Program().version, 'unknown')
def may_specify_version(self): eq_(Program(version='1.2.3').version, '1.2.3')
def uses_executor_class_given(self): klass = Mock() Program(executor_class=klass).run("myapp foo", exit=False) klass.assert_called_with(ANY, ANY, ANY) klass.return_value.execute.assert_called_with(ANY)
def displays_name_and_version(self): expect("--version", program=Program(name="MyProgram", version='0.1.0'), out="MyProgram 0.1.0\n")
def uses_overridden_value_when_given(self): expect("myapp --help", out="nope [--core-opts]", program=Program(binary='nope'), invoke=False, test=assert_contains)
def splits_a_string(self): p = Program() p.print_version = Mock() p.run("inv --version", exit=False) p.print_version.assert_called()
from invoke import Collection, Program from nebula import tasks program = Program(namespace=Collection.from_module(tasks), version="0.0.1")
def config_class_defaults_to_Config(self): ok_(Program().config_class is Config)
def _test_flag(self, flag, key, value=True): p = Program() p.execute = Mock() # neuter p.run('inv {0} foo'.format(flag)) eq_(p.config.run[key], value)
def may_specify_executor_class(self): klass = object() eq_(Program(executor_class=klass).executor_class, klass) # noqa
def executor_class_defaults_to_Executor(self): ok_(Program().executor_class is Executor)
def may_specify_loader_class(self): klass = object() eq_(Program(loader_class=klass).loader_class, klass)
def loader_class_defaults_to_FilesystemLoader(self): ok_(Program().loader_class is FilesystemLoader)
def honors_program_binary(self): expect('-c decorator -h biz', out="Usage: notinvoke", test=assert_contains, program=Program(binary='notinvoke'))
def may_specify_binary(self): eq_(Program(binary='myapp').binary, 'myapp')
value="", workspace="", organisation=None, token=None): """Update variable in Terraform cloud.""" organisation = organisation or os.getenv("TERRAFORM_USER") token = token or os.getenv("TERRAFORM_TOKEN") assert organisation is not None, "Missing Terraform Cloud organisation." assert token is not None, "Missing Terraform Cloud token." api.update_workspace_variable(organisation, workspace, token, variable, value) print(f"Updated Terraform {variable} for {workspace}") @task def run(ctx, message="", workspace="", organisation=None, token=None): """Run a plan in Terraform cloud.""" organisation = organisation or os.getenv("TERRAFORM_USER") token = token or os.getenv("TERRAFORM_TOKEN") assert organisation is not None, "Missing Terraform Cloud organisation." assert token is not None, "Missing Terraform Cloud token." url = api.run_workspace_plan(organisation, workspace, token, message) print(f"Running Terraform plan for {workspace}: {url}") ns = Collection() ns.add_task(update) ns.add_task(run) main = Program(namespace=ns, version=__version__)
def config_class_init_kwarg_is_honored(self): klass = self._klass() Program(config_class=klass).run("myapp foo", exit=False) eq_(len(klass.call_args_list), 1) # don't care about actual args
def uses_a_list_unaltered(self): p = Program() p.print_version = Mock() p.run(['inv', '--version'], exit=False) p.print_version.assert_called()
def may_specify_config_class(self): klass = object() eq_(Program(config_class=klass).config_class, klass) # noqa
def uses_overridden_value_when_given(self): p = Program(name='NotInvoke') expect("--version", out="NotInvoke unknown\n", program=p)
from invoke import Program from tester import tasks program = Program(namespace=tasks, version='0.1.0')
def use_binary_basename_when_invoked_absolutely(self): Program().run("/usr/local/bin/myapp --help", exit=False) stdout = sys.stdout.getvalue() assert_contains(stdout, "myapp [--core-opts]") assert_not_contains(stdout, "/usr/local/bin")
def may_specify_namespace(self): foo = load('foo') ok_(Program(namespace=foo).namespace is foo)
def uses_loader_class_given(self): klass = Mock(side_effect=FilesystemLoader) Program(loader_class=klass).run("myapp --help foo", exit=False) klass.assert_called_with(start=ANY, config=ANY)
def does_not_seek_tasks_module_if_namespace_was_given(self): expect('foo', err="No idea what 'foo' is!\n", program=Program(namespace=Collection('blank')))
def null_namespace_triggers_task_related_args(self): program = Program(namespace=None) for arg in program.task_args(): stdout, _ = run("--help", program=program) assert arg.name in stdout
def turns_KeyboardInterrupt_into_exit_code_1(self, mock_exit): p = Program() p.execute = Mock(side_effect=KeyboardInterrupt) p.run("myapp -c foo mytask") mock_exit.assert_called_with(1)
from . import * from invoke import Program import toml, os try: pyproject = toml.load(f"{os.getcwd()}/pyproject.toml", _dict=dict) version = pyproject["tool"]["poetry"]["version"] except FileNotFoundError: version = None program = Program( name="mycli", namespace=ns, version=version, binary="mycli", binary_names=["mycli"], )
# Using `invoke` as a library # http://docs.pyinvoke.org/en/stable/concepts/library.html from invoke import Collection, Config, Program from . import docker, git, helm, local, python, terraform __version__ = "0.0.8" class BaseConfig(Config): prefix = "noosinv" ns = Collection() ns.add_collection(docker.ns) ns.add_collection(git.ns) ns.add_collection(helm.ns) ns.add_collection(local) ns.add_collection(python.ns) ns.add_collection(terraform.ns) main = Program(namespace=ns, config_class=BaseConfig, version=__version__)
def may_specify_name(self): eq_(Program(name='Myapp').name, 'Myapp')
import sentry_sdk from invoke import Collection, Program from sentry_sdk.integrations.aiohttp import AioHttpIntegration from sentry_sdk.integrations.celery import CeleryIntegration from usb import cli, server, tasks from usb.bot import discord __version__ = "0.0.12" sentry_sdk.init( integrations=[CeleryIntegration(), AioHttpIntegration()], release=f"usb@{__version__}", ) program = Program(namespace=Collection(cli, server, tasks, discord), version=__version__)
def main(): program = Program(namespace=Collection.from_module(sys.modules[__name__]), version='0.1.0') program.run()
def turns_KeyboardInterrupt_into_exit_code_130(self, mock_exit): p = Program() p.execute = Mock(side_effect=KeyboardInterrupt) p.run("myapp -c foo mytask") mock_exit.assert_called_with(130)
def _test_flag(self, flag, key, value=True): p = Program() p.execute = Mock() # neuter p.run("inv {} foo".format(flag)) assert p.config.run[key] == value