def main(): """Invokes the tasks defined in _tasks.py, with some configuration.""" try: from invoke import Collection, Program # isort:skip from sdsstools import _tasks # isort:skip except (ImportError, ModuleNotFoundError): raise ImportError("Cannot find invoke. Make sure sdsstools " "is installed for development.") # Use the metadata file to determine the root of the package. metadata_file = get_metadata_files(".") if metadata_file is None: raise RuntimeError("cannot find the root of the package.") os.chdir(os.path.dirname(metadata_file)) # Override the configuration if there is an invoke.yaml file next to the # metadata file. if os.path.exists("./invoke.yaml"): config = yaml.safe_load(open("invoke.yaml")) print("Using configuration file invoke.yaml") else: config = None program = Program(version=__version__, namespace=Collection.from_module(_tasks, config=config)) program.run()
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 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 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 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 shows_UnexpectedExit_str_when_streams_hidden(self, mock_exit): p = Program() oops = UnexpectedExit( Result( command="meh", exited=54, stdout="things!", stderr="ohnoz!", encoding="utf-8", 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. stderr = sys.stderr.getvalue() assert (stderr == """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 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 {}".format(invocation) program.run(invocation, exit=False) # Perform tests if out is not None: (test or eq_)(sys.stdout.getvalue(), out) stderr = sys.stderr.getvalue() if err is not None: (test or eq_)(stderr, err) # Guard against silent failures; since we say exit=False this is the only # real way to tell if stuff died in a manner we didn't expect. elif stderr: assert False, "Unexpected stderr: {}".format(stderr)
def test_task_with_one_arg_not_given(capsys): programme = Programme(namespace=myapp()) programme.run("invoke echo", exit=False) captured = capsys.readouterr() assert ( captured.err == "'echo' did not receive required positional arguments: 'word'\n" ) assert captured.out == ""
def main(): ns = Collection() ns.add_task(get) ns.add_task(setpasswd) ns.add_task(configure) ns.add_task(list) ns.add_task(delete) program = Program(version=VERSION, namespace=ns) program.run()
def config_attribute_is_memoized(self): klass = self._klass() # Can't .config without .run (meh); .run calls .config once. p = Program(config_class=klass) p.run("myapp foo", exit=False) assert klass.call_count == 1 # Second access should use cached value p.config assert klass.call_count == 1
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 main() -> None: # This says importlib.util.find_spec() can test if a module is currently importable: # https://docs.python.org/3/library/importlib.html#importlib.util.find_spec if not importlib.util.find_spec("invoke"): ensure_invoke() from invoke import task, Program, Config, Collection from invoke.config import merge_dicts from invoke.watchers import Responder namespace = Collection() globs = dict(globals()) namespace.add_task(task(atask_default, post=[ atask_install_scoop, atask_install_keepass, atask_setup_keepass, atask_schedule_updates, ]), default=True) for i,name in enumerate(namespace.tasks["atask-default"].post): namespace.tasks["atask-default"].post[i] = task(name) namespace.add_task(task(name)) class SetupConfig(Config): prefix: str = PROG_NAME @staticmethod def global_defaults(): base_defaults = Config.global_defaults() overrides = { "tasks": {"collection_name": PROG_NAME}, "run": { "shell": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "echo": True, "debug": True, }, } return merge_dicts(base=base_defaults, updates=overrides) program = Program( name=PROG_NAME, namespace=namespace, config_class=SetupConfig, version="0.0.1" ) # NOTE: Debug # This uses the Python auditing framework in Python 3.8+ if sys.version_info>=(3,8): print("auditing enabled") def print_popen(*args, **kwargs) -> None: if args[0] == "subprocess.Popen": # sys.audit("subprocess.Popen", executable, args, cwd, env) # ("subprocess.Popen", (executable, args, cwd, env)) print(f"{args[1][0]} -> {args[1][1]}") sys.addaudithook(print_popen) program.run()
def test_app_setting_different_default(capsys): def myapp2(): ns = Collection() add_task(ns, default, word="appdefault") return ns programme = Programme(namespace=myapp2()) programme.run("invoke default", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == "appdefault\n"
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 {}".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 '{}' is!\n".format(nah)) 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 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 env_var_prefix_can_be_overridden(self): os.environ['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 xtest_help(capsys): programme = Programme(namespace=myapp()) assert default.help == programme.namespace["default"].help programme.run("invoke --help", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == dedent( """\ Usage: invoke [--core-opts] <subcommand> [--subcommand-opts] ... Core options: --complete Print tab-completion candidates for given parse remainder. --hide=STRING Set default value of run()'s 'hide' kwarg. --print-completion-script=STRING Print the tab-completion script for your preferred shell (bash|zsh|fish). --prompt-for-sudo-password Prompt user at start of session for the sudo.password config value. --write-pyc Enable creation of .pyc files. -d, --debug Enable debug output. -D INT, --list-depth=INT When listing tasks, only show the first INT levels. -e, --echo Echo executed commands before running. -f STRING, --config=STRING Runtime configuration file to use. -F STRING, --list-format=STRING Change the display format used when listing tasks. Should be one of: flat (default), nested, json. -h [STRING], --help[=STRING] Show core or per-task help and exit. -l [STRING], --list[=STRING] List available tasks, optionally limited to a namespace. -p, --pty Use a pty when executing shell commands. -R, --dry Echo commands instead of running. -T INT, --command-timeout=INT Specify a global command execution timeout, in seconds. -V, --version Show version and exit. -w, --warn-only Warn, instead of failing, when shell commands fail. Subcommands: default Echo the given word. Defaults to 'defaultword'. echo Echo the given word. foo """ )
def test_task_with_no_args_help(capsys): programme = Programme(namespace=myapp()) programme.run("invoke foo -h", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == dedent( """\ Usage: invoke [--core-opts] foo [other tasks here ...] Docstring: none Options: none """ )
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 test_task_with_one_arg_help(capsys): programme = Programme(namespace=myapp()) programme.run("invoke --help echo", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == dedent( """\ Usage: invoke [--core-opts] echo [--options] [other tasks here ...] Docstring: Echo the given word. Options: -w STRING, --word=STRING """ )
def main(): config = { 'run': { 'echo': True }, 'NINJA_STATUS': '[%f/%t (%p) %es]' # make the ninja output even nicer } ns = Collection.from_module(tasks, config=config) ns.add_collection( Collection.from_module(setupenv, name='setup-dev-env', config=config)) p = Program(binary='m(mongodb command line tool)', name='MongoDB Command Line Tool', namespace=ns, version='1.0.0-alpha2') p.run()
def run(): invoke_config = { 'run': { 'hide': True # Don't print stdout or stderr. }, 'NINJA_STATUS': '[%f/%t (%p) %es] ' # make the ninja output even nicer } ns = Collection.from_module(tasks, config=invoke_config) ns.add_collection( Collection.from_module(setupenv, name='setup', config=invoke_config)) ns.add_collection( Collection.from_module(helpers, name='helpers', config=invoke_config)) proj_info = pkg_resources.require("server_workflow_tool")[0] p = Program(binary='workflow', name=proj_info.project_name, namespace=ns, version=proj_info.version) p.parse_core(sys.argv[1:]) if p.args.debug.value: get_logger(level=logging.DEBUG) else: get_logger(level=logging.INFO) c = Config() try: p.run() except (InvalidConfigError, RequireUserInputError): # These errors are not actionable right now. sys.exit(1) finally: c.dump()
def test_task_with_no_args(capsys): programme = Programme(namespace=myapp()) programme.run("invoke foo", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == "foo\n"
def is_basename_when_given_a_path(self): p = Program() p.run("/usr/local/bin/whatever --help", exit=False) assert p.called_as == "whatever"
def is_the_whole_deal_when_just_a_name(self): p = Program() p.run("whatever --help", exit=False) assert p.called_as == "whatever"
def splits_a_string(self): p = Program() p.print_version = Mock() p.run("inv --version", exit=False) p.print_version.assert_called()
def main(): program = Program(namespace=Collection.from_module(sys.modules[__name__]), version='0.1.0') program.run()
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
def uses_a_list_unaltered(self): p = Program() p.print_version = Mock() p.run(["inv", "--version"], exit=False) p.print_version.assert_called()
def uses_a_list_unaltered(self): p = Program() p.print_version = Mock() p.run(['inv', '--version'], exit=False) p.print_version.assert_called()
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 {0} foo'.format(flag)) eq_(p.config.run[key], value)
def test_task_with_one_kw_arg_not_given(capsys): programme = Programme(namespace=myapp()) programme.run("invoke default", exit=False) captured = capsys.readouterr() assert captured.err == "" assert captured.out == "defaultword\n"
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)