invoke 2.2.1__tar.gz → 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {invoke-2.2.1 → invoke-3.0.0}/MANIFEST.in +1 -1
- {invoke-2.2.1/invoke.egg-info → invoke-3.0.0}/PKG-INFO +18 -21
- {invoke-2.2.1 → invoke-3.0.0}/README.rst +8 -4
- {invoke-2.2.1 → invoke-3.0.0}/invoke/__init__.py +9 -7
- {invoke-2.2.1 → invoke-3.0.0}/invoke/config.py +1 -1
- {invoke-2.2.1 → invoke-3.0.0}/invoke/context.py +49 -35
- {invoke-2.2.1 → invoke-3.0.0}/invoke/executor.py +8 -5
- {invoke-2.2.1 → invoke-3.0.0}/invoke/loader.py +1 -1
- {invoke-2.2.1 → invoke-3.0.0}/invoke/program.py +2 -2
- {invoke-2.2.1 → invoke-3.0.0}/invoke/runners.py +75 -31
- {invoke-2.2.1 → invoke-3.0.0}/invoke/tasks.py +15 -9
- {invoke-2.2.1 → invoke-3.0.0/invoke.egg-info}/PKG-INFO +18 -21
- {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/SOURCES.txt +0 -3
- invoke-3.0.0/pyproject.toml +108 -0
- invoke-3.0.0/sites/docs/.readthedocs.yaml +14 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/invoking-tasks.rst +54 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/conf.py +12 -1
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/invoke.rst +15 -1
- {invoke-2.2.1 → invoke-3.0.0}/sites/shared_conf.py +2 -3
- invoke-3.0.0/sites/www/.readthedocs.yaml +14 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/changelog.rst +77 -6
- invoke-3.0.0/sites/www/contact.rst +11 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/development.rst +8 -5
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/index.rst +5 -5
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/installing.rst +6 -7
- {invoke-2.2.1 → invoke-3.0.0}/tasks.py +50 -20
- {invoke-2.2.1 → invoke-3.0.0}/tests/_util.py +1 -1
- {invoke-2.2.1 → invoke-3.0.0}/tests/collection.py +1 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/config.py +2 -1
- {invoke-2.2.1 → invoke-3.0.0}/tests/context.py +10 -8
- {invoke-2.2.1 → invoke-3.0.0}/tests/executor.py +4 -5
- {invoke-2.2.1 → invoke-3.0.0}/tests/init.py +1 -14
- {invoke-2.2.1 → invoke-3.0.0}/tests/parser_context.py +1 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/parser_parser.py +78 -72
- {invoke-2.2.1 → invoke-3.0.0}/tests/program.py +24 -26
- {invoke-2.2.1 → invoke-3.0.0}/tests/runners.py +45 -16
- {invoke-2.2.1 → invoke-3.0.0}/tests/task.py +19 -7
- {invoke-2.2.1 → invoke-3.0.0}/tests/util.py +2 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/watchers.py +1 -0
- invoke-2.2.1/dev-requirements.txt +0 -23
- invoke-2.2.1/invoke/_version.py +0 -2
- invoke-2.2.1/pyproject.toml +0 -52
- invoke-2.2.1/setup.py +0 -78
- invoke-2.2.1/sites/docs/.readthedocs.yaml +0 -13
- invoke-2.2.1/sites/www/.readthedocs.yaml +0 -13
- invoke-2.2.1/sites/www/contact.rst +0 -25
- {invoke-2.2.1 → invoke-3.0.0}/LICENSE +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/__main__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/collection.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/bash.completion +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/complete.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/fish.completion +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/zsh.completion +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/env.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/exceptions.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/main.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/argument.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/context.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/parser.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/py.typed +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/terminals.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/util.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/backwardscompat.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/machine.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/_version.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/alias_dict.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/attribute_dict.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/composer.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/constructor.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/cyaml.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/dumper.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/emitter.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/error.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/events.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/loader.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/nodes.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/parser.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/reader.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/representer.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/resolver.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/scanner.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/serializer.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/tokens.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke/watchers.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/dependency_links.txt +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/entry_points.txt +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/top_level.txt +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/setup.cfg +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/_static/rtd.css +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/__init__.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/collection.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/config.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/context.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/exceptions.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/executor.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/loader.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/parser.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/program.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/runners.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/tasks.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/terminals.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/util.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/watchers.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/configuration.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/library.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/loading.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/namespaces.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/testing.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/watchers.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/getting-started.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/docs/index.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/conf.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/faq.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/sites/www/prior-art.rst +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/alias_sorting.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/autoprint.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/branch/explicit.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/branch/tasks.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.json +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.yml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/collection.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/echo.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json/invoke.json +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json-and-python/invoke.json +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json-and-python/invoke.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/nested/invoke.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/no-dedupe.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/no-echo.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/package/invoke.yml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/package/tasks/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/python/invoke.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/runtime.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.json +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.yml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/underscores/invoke.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/underscores/tasks.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/explicit.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/invoke.yaml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/tasks.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/explicit.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/invoke.yml +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/tasks.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/contextualized.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/custom_executor.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/debugging.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/decorator_multi_default.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/decorators.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/deeper_ns_list.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/depth_first.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/docstrings.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/empty.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/empty_subcollection.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/explicit_root.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/foo.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/has_modules.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/ignoreme/ignoremetoo/.no +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/integration.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/namespacing.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/nontrivial_docstrings.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/oops.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/package/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/package/module.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/simple_ns_list.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subcollection_task_name.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subspace/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subspace/module.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/sudo_prompt.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tasks.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/__init__.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/docs.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/python.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/deploy.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/provision.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree.json +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/cli.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/completion.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/concurrency.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/conftest.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/loader.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/merge_dicts.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/parser_argument.py +0 -0
- {invoke-2.2.1 → invoke-3.0.0}/tests/terminals.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
include LICENSE
|
|
2
2
|
include README.rst
|
|
3
3
|
include tasks.py
|
|
4
|
+
include pyproject.toml
|
|
4
5
|
recursive-include invoke/completion *
|
|
5
6
|
recursive-include sites *
|
|
6
7
|
recursive-exclude sites/*/_build *
|
|
7
|
-
include dev-requirements.txt
|
|
8
8
|
recursive-include * py.typed
|
|
9
9
|
recursive-include tests *
|
|
10
10
|
recursive-exclude * *.pyc *.pyo
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: invoke
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Pythonic task execution
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Author-email: jeff@bitprophet.org
|
|
8
|
-
License: BSD
|
|
5
|
+
Author-email: Jeff Forcier <jeff@bitprophet.org>
|
|
6
|
+
License-Expression: BSD-2-Clause
|
|
9
7
|
Project-URL: Docs, https://docs.pyinvoke.org
|
|
10
8
|
Project-URL: Source, https://github.com/pyinvoke/invoke
|
|
11
|
-
Project-URL: Issues, https://github.com/pyinvoke/invoke/issues
|
|
12
9
|
Project-URL: Changelog, https://www.pyinvoke.org/changelog.html
|
|
13
10
|
Project-URL: CI, https://app.circleci.com/pipelines/github/pyinvoke/invoke
|
|
11
|
+
Project-URL: Issues, https://github.com/pyinvoke/invoke/issues
|
|
14
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
15
13
|
Classifier: Environment :: Console
|
|
16
14
|
Classifier: Intended Audience :: Developers
|
|
17
15
|
Classifier: Intended Audience :: System Administrators
|
|
18
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
19
16
|
Classifier: Operating System :: POSIX
|
|
20
17
|
Classifier: Operating System :: Unix
|
|
21
18
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
@@ -23,21 +20,22 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
23
20
|
Classifier: Programming Language :: Python
|
|
24
21
|
Classifier: Programming Language :: Python :: 3
|
|
25
22
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
27
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
28
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
29
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
30
24
|
Classifier: Programming Language :: Python :: 3.10
|
|
31
25
|
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
32
29
|
Classifier: Topic :: Software Development
|
|
33
30
|
Classifier: Topic :: Software Development :: Build Tools
|
|
34
31
|
Classifier: Topic :: Software Development :: Libraries
|
|
35
32
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
36
33
|
Classifier: Topic :: System :: Software Distribution
|
|
37
34
|
Classifier: Topic :: System :: Systems Administration
|
|
38
|
-
Requires-Python: >=3.
|
|
35
|
+
Requires-Python: >=3.9
|
|
36
|
+
Description-Content-Type: text/x-rst
|
|
39
37
|
License-File: LICENSE
|
|
40
|
-
|
|
38
|
+
Dynamic: license-file
|
|
41
39
|
|
|
42
40
|
|version| |python| |license| |ci| |coverage|
|
|
43
41
|
|
|
@@ -60,18 +58,17 @@ License-File: LICENSE
|
|
|
60
58
|
Welcome to Invoke!
|
|
61
59
|
==================
|
|
62
60
|
|
|
63
|
-
Invoke is a Python
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
Invoke is a Python library for managing shell-oriented subprocesses and
|
|
62
|
+
organizing executable Python code into CLI-invokable tasks. It draws
|
|
63
|
+
inspiration from various sources (``make``/``rake``, Fabric 1.x, etc) to arrive
|
|
64
|
+
at a powerful & clean feature set.
|
|
67
65
|
|
|
68
66
|
To find out what's new in this version of Invoke, please see `the changelog
|
|
69
67
|
<https://pyinvoke.org/changelog.html#{}>`_.
|
|
70
68
|
|
|
71
|
-
The project maintainer keeps a `roadmap
|
|
72
|
-
<https://bitprophet.org/projects#roadmap>`_ on his website.
|
|
73
|
-
|
|
74
|
-
|
|
75
69
|
For a high level introduction, including example code, please see `our main
|
|
76
70
|
project website <https://pyinvoke.org>`_; or for detailed API docs, see `the
|
|
77
71
|
versioned API website <https://docs.pyinvoke.org>`_.
|
|
72
|
+
|
|
73
|
+
The project maintainer keeps a `roadmap
|
|
74
|
+
<https://bitprophet.org/projects#roadmap>`_ on his website.
|
|
@@ -19,13 +19,17 @@
|
|
|
19
19
|
Welcome to Invoke!
|
|
20
20
|
==================
|
|
21
21
|
|
|
22
|
-
Invoke is a Python
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
Invoke is a Python library for managing shell-oriented subprocesses and
|
|
23
|
+
organizing executable Python code into CLI-invokable tasks. It draws
|
|
24
|
+
inspiration from various sources (``make``/``rake``, Fabric 1.x, etc) to arrive
|
|
25
|
+
at a powerful & clean feature set.
|
|
26
26
|
|
|
27
27
|
To find out what's new in this version of Invoke, please see `the changelog
|
|
28
28
|
<https://pyinvoke.org/changelog.html#{}>`_.
|
|
29
29
|
|
|
30
|
+
For a high level introduction, including example code, please see `our main
|
|
31
|
+
project website <https://pyinvoke.org>`_; or for detailed API docs, see `the
|
|
32
|
+
versioned API website <https://docs.pyinvoke.org>`_.
|
|
33
|
+
|
|
30
34
|
The project maintainer keeps a `roadmap
|
|
31
35
|
<https://bitprophet.org/projects#roadmap>`_ on his website.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from importlib import metadata
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
|
-
from ._version import __version_info__, __version__ # noqa
|
|
4
4
|
from .collection import Collection # noqa
|
|
5
5
|
from .config import Config # noqa
|
|
6
6
|
from .context import Context, MockContext # noqa
|
|
@@ -8,6 +8,7 @@ from .exceptions import ( # noqa
|
|
|
8
8
|
AmbiguousEnvVar,
|
|
9
9
|
AuthFailure,
|
|
10
10
|
CollectionNotFound,
|
|
11
|
+
CommandTimedOut,
|
|
11
12
|
Exit,
|
|
12
13
|
ParseError,
|
|
13
14
|
PlatformError,
|
|
@@ -19,19 +20,20 @@ from .exceptions import ( # noqa
|
|
|
19
20
|
UnknownFileType,
|
|
20
21
|
UnpicklableConfigMember,
|
|
21
22
|
WatcherError,
|
|
22
|
-
CommandTimedOut,
|
|
23
23
|
)
|
|
24
24
|
from .executor import Executor # noqa
|
|
25
25
|
from .loader import FilesystemLoader # noqa
|
|
26
26
|
from .parser import Argument, Parser, ParserContext, ParseResult # noqa
|
|
27
27
|
from .program import Program # noqa
|
|
28
|
-
from .runners import
|
|
29
|
-
from .tasks import
|
|
28
|
+
from .runners import Failure, Local, Promise, Result, Runner # noqa
|
|
29
|
+
from .tasks import Call, Task, call, task # noqa
|
|
30
30
|
from .terminals import pty_size # noqa
|
|
31
31
|
from .watchers import FailingResponder, Responder, StreamWatcher # noqa
|
|
32
32
|
|
|
33
|
+
__version__ = metadata.version("invoke")
|
|
34
|
+
|
|
33
35
|
|
|
34
|
-
def run(command: str, **kwargs: Any) ->
|
|
36
|
+
def run(command: str, **kwargs: Any) -> Result:
|
|
35
37
|
"""
|
|
36
38
|
Run ``command`` in a subprocess and return a `.Result` object.
|
|
37
39
|
|
|
@@ -50,7 +52,7 @@ def run(command: str, **kwargs: Any) -> Optional[Result]:
|
|
|
50
52
|
return Context().run(command, **kwargs)
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
def sudo(command: str, **kwargs: Any) ->
|
|
55
|
+
def sudo(command: str, **kwargs: Any) -> Result:
|
|
54
56
|
"""
|
|
55
57
|
Run ``command`` in a ``sudo`` subprocess and return a `.Result` object.
|
|
56
58
|
|
|
@@ -449,7 +449,7 @@ class Config(DataProxy):
|
|
|
449
449
|
# TODO: consider an automatic fallback to /bin/sh for systems lacking
|
|
450
450
|
# /bin/bash; however users may configure run.shell quite easily, so...
|
|
451
451
|
else:
|
|
452
|
-
shell = "
|
|
452
|
+
shell = "bash"
|
|
453
453
|
|
|
454
454
|
return {
|
|
455
455
|
# TODO: we document 'debug' but it's not truly implemented outside
|
|
@@ -4,7 +4,6 @@ from contextlib import contextmanager
|
|
|
4
4
|
from itertools import cycle
|
|
5
5
|
from os import PathLike
|
|
6
6
|
from typing import (
|
|
7
|
-
TYPE_CHECKING,
|
|
8
7
|
Any,
|
|
9
8
|
Generator,
|
|
10
9
|
Iterator,
|
|
@@ -15,13 +14,10 @@ from typing import (
|
|
|
15
14
|
from unittest.mock import Mock
|
|
16
15
|
|
|
17
16
|
from .config import Config, DataProxy
|
|
18
|
-
from .exceptions import
|
|
19
|
-
from .runners import Result
|
|
17
|
+
from .exceptions import AuthFailure, Failure, ResponseNotAccepted
|
|
18
|
+
from .runners import Result, Runner
|
|
20
19
|
from .watchers import FailingResponder
|
|
21
20
|
|
|
22
|
-
if TYPE_CHECKING:
|
|
23
|
-
from invoke.runners import Runner
|
|
24
|
-
|
|
25
21
|
|
|
26
22
|
class Context(DataProxy):
|
|
27
23
|
"""
|
|
@@ -43,37 +39,59 @@ class Context(DataProxy):
|
|
|
43
39
|
.. versionadded:: 1.0
|
|
44
40
|
"""
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
# NOTE: sometime after Sphinx 1.7, autodoc stopped being able to see
|
|
43
|
+
# doc-comments inside __init__ (or something equivalent, anyway). Moving
|
|
44
|
+
# the type definitions up here seems to work better and /shouldn't/ mess up
|
|
45
|
+
# the DataProxy magic going on...
|
|
46
|
+
|
|
47
|
+
#: A list of commands to run (via "&&") before the main argument to any
|
|
48
|
+
#: `run` or `sudo` calls. Note that the primary API for manipulating
|
|
49
|
+
#: this list is `prefix`; see its docs for details.
|
|
50
|
+
command_prefixes: List[str]
|
|
51
|
+
#: A list of directories to 'cd' into before running commands with
|
|
52
|
+
#: `run` or `sudo`; intended for management via `cd`, please see its
|
|
53
|
+
#: docs for details.
|
|
54
|
+
command_cwds: List[str]
|
|
55
|
+
#: The CLI parser's 'remainder' value (text given after a standalone
|
|
56
|
+
#: ``--`` in the command line), or the empty string if not given.
|
|
57
|
+
remainder: str
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
config: Optional[Config] = None,
|
|
62
|
+
remainder: str = "",
|
|
63
|
+
) -> None:
|
|
47
64
|
"""
|
|
48
65
|
:param config:
|
|
49
66
|
`.Config` object to use as the base configuration.
|
|
50
67
|
|
|
51
68
|
Defaults to an anonymous/default `.Config` instance.
|
|
69
|
+
|
|
70
|
+
:param remainder:
|
|
71
|
+
The invoking program's :ref:`parser remainder <remainder>` value,
|
|
72
|
+
if any was obtained.
|
|
52
73
|
"""
|
|
53
|
-
#: The fully merged `.Config` object appropriate for this context.
|
|
54
|
-
#:
|
|
55
|
-
#: `.Config` settings (see their documentation for details) may be
|
|
56
|
-
#: accessed like dictionary keys (``c.config['foo']``) or object
|
|
57
|
-
#: attributes (``c.config.foo``).
|
|
58
|
-
#:
|
|
59
|
-
#: As a convenience shorthand, the `.Context` object proxies to its
|
|
60
|
-
#: ``config`` attribute in the same way - e.g. ``c['foo']`` or
|
|
61
|
-
#: ``c.foo`` returns the same value as ``c.config['foo']``.
|
|
62
74
|
config = config if config is not None else Config()
|
|
63
|
-
self._set(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
#: A list of directories to 'cd' into before running commands with
|
|
70
|
-
#: `run` or `sudo`; intended for management via `cd`, please see its
|
|
71
|
-
#: docs for details.
|
|
72
|
-
command_cwds: List[str] = list()
|
|
73
|
-
self._set(command_cwds=command_cwds)
|
|
75
|
+
self._set(
|
|
76
|
+
_config=config,
|
|
77
|
+
command_prefixes=[],
|
|
78
|
+
command_cwds=[],
|
|
79
|
+
remainder=remainder,
|
|
80
|
+
)
|
|
74
81
|
|
|
75
82
|
@property
|
|
76
83
|
def config(self) -> Config:
|
|
84
|
+
"""
|
|
85
|
+
The fully merged `.Config` object appropriate for this context.
|
|
86
|
+
|
|
87
|
+
`.Config` settings (see their documentation for details) may be
|
|
88
|
+
accessed like dictionary keys (``c.config['foo']``) or object
|
|
89
|
+
attributes (``c.config.foo``).
|
|
90
|
+
|
|
91
|
+
As a convenience shorthand, the `.Context` object proxies to its
|
|
92
|
+
``config`` attribute in the same way - e.g. ``c['foo']`` or
|
|
93
|
+
``c.foo`` returns the same value as ``c.config['foo']``.
|
|
94
|
+
"""
|
|
77
95
|
# Allows Context to expose a .config attribute even though DataProxy
|
|
78
96
|
# otherwise considers it a config key.
|
|
79
97
|
return self._config
|
|
@@ -87,7 +105,7 @@ class Context(DataProxy):
|
|
|
87
105
|
# runtime.
|
|
88
106
|
self._set(_config=value)
|
|
89
107
|
|
|
90
|
-
def run(self, command: str, **kwargs: Any) ->
|
|
108
|
+
def run(self, command: str, **kwargs: Any) -> Result:
|
|
91
109
|
"""
|
|
92
110
|
Execute a local shell command, honoring config options.
|
|
93
111
|
|
|
@@ -106,13 +124,11 @@ class Context(DataProxy):
|
|
|
106
124
|
# NOTE: broken out of run() to allow for runner class injection in
|
|
107
125
|
# Fabric/etc, which needs to juggle multiple runner class types (local and
|
|
108
126
|
# remote).
|
|
109
|
-
def _run(
|
|
110
|
-
self, runner: "Runner", command: str, **kwargs: Any
|
|
111
|
-
) -> Optional[Result]:
|
|
127
|
+
def _run(self, runner: "Runner", command: str, **kwargs: Any) -> Result:
|
|
112
128
|
command = self._prefix_commands(command)
|
|
113
129
|
return runner.run(command, **kwargs)
|
|
114
130
|
|
|
115
|
-
def sudo(self, command: str, **kwargs: Any) ->
|
|
131
|
+
def sudo(self, command: str, **kwargs: Any) -> Result:
|
|
116
132
|
"""
|
|
117
133
|
Execute a shell command via ``sudo`` with password auto-response.
|
|
118
134
|
|
|
@@ -185,9 +201,7 @@ class Context(DataProxy):
|
|
|
185
201
|
return self._sudo(runner, command, **kwargs)
|
|
186
202
|
|
|
187
203
|
# NOTE: this is for runner injection; see NOTE above _run().
|
|
188
|
-
def _sudo(
|
|
189
|
-
self, runner: "Runner", command: str, **kwargs: Any
|
|
190
|
-
) -> Optional[Result]:
|
|
204
|
+
def _sudo(self, runner: "Runner", command: str, **kwargs: Any) -> Result:
|
|
191
205
|
prompt = self.config.sudo.prompt
|
|
192
206
|
password = kwargs.pop("password", self.config.sudo.password)
|
|
193
207
|
user = kwargs.pop("user", self.config.sudo.user)
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
|
2
2
|
|
|
3
3
|
from .config import Config
|
|
4
|
-
from .parser import ParserContext
|
|
5
|
-
from .util import debug
|
|
4
|
+
from .parser import ParserContext, ParseResult
|
|
6
5
|
from .tasks import Call, Task
|
|
6
|
+
from .util import debug
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from .collection import Collection
|
|
10
10
|
from .runners import Result
|
|
11
|
-
from .parser import ParseResult
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class Executor:
|
|
@@ -41,10 +40,14 @@ class Executor:
|
|
|
41
40
|
:param core:
|
|
42
41
|
An optional `.ParseResult` holding parsed core program arguments.
|
|
43
42
|
Defaults to ``None``.
|
|
43
|
+
|
|
44
|
+
.. versionchanged:: 3.0
|
|
45
|
+
The ``core`` attribute now defaults to an 'empty' `.ParseResult` if
|
|
46
|
+
``None`` was given.
|
|
44
47
|
"""
|
|
45
48
|
self.collection = collection
|
|
46
49
|
self.config = config if config is not None else Config()
|
|
47
|
-
self.core = core
|
|
50
|
+
self.core = core if core is not None else ParseResult()
|
|
48
51
|
|
|
49
52
|
def execute(
|
|
50
53
|
self, *tasks: Union[str, Tuple[str, Dict[str, Any]], ParserContext]
|
|
@@ -135,7 +138,7 @@ class Executor:
|
|
|
135
138
|
# Get final context from the Call (which will know how to generate
|
|
136
139
|
# an appropriate one; e.g. subclasses might use extra data from
|
|
137
140
|
# being parameterized), handing in this config for use there.
|
|
138
|
-
context = call.make_context(config)
|
|
141
|
+
context = call.make_context(config, core_parse_result=self.core)
|
|
139
142
|
args = (context, *call.args)
|
|
140
143
|
result = call.task(*args, **call.kwargs)
|
|
141
144
|
if autoprint:
|
|
@@ -37,7 +37,7 @@ class Loader:
|
|
|
37
37
|
|
|
38
38
|
Must return a ModuleSpec valid for use by `importlib`, which is
|
|
39
39
|
typically a name string followed by the contents of the 3-tuple
|
|
40
|
-
returned by `importlib.module_from_spec` (``name``, ``loader``,
|
|
40
|
+
returned by `importlib.util.module_from_spec` (``name``, ``loader``,
|
|
41
41
|
``origin``.)
|
|
42
42
|
|
|
43
43
|
For a sample implementation, see `.FilesystemLoader`.
|
|
@@ -18,8 +18,8 @@ from typing import (
|
|
|
18
18
|
|
|
19
19
|
from . import Collection, Config, Executor, FilesystemLoader
|
|
20
20
|
from .completion.complete import complete, print_completion_script
|
|
21
|
-
from .
|
|
22
|
-
from .
|
|
21
|
+
from .exceptions import CollectionNotFound, Exit, ParseError, UnexpectedExit
|
|
22
|
+
from .parser import Argument, Parser, ParserContext
|
|
23
23
|
from .terminals import pty_size
|
|
24
24
|
from .util import debug, enable_logging, helpline
|
|
25
25
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import errno
|
|
2
2
|
import locale
|
|
3
3
|
import os
|
|
4
|
+
import signal
|
|
4
5
|
import struct
|
|
5
6
|
import sys
|
|
6
7
|
import threading
|
|
7
8
|
import time
|
|
8
|
-
import
|
|
9
|
-
from subprocess import Popen, PIPE
|
|
9
|
+
from subprocess import PIPE, Popen
|
|
10
10
|
from types import TracebackType
|
|
11
11
|
from typing import (
|
|
12
|
+
IO,
|
|
12
13
|
TYPE_CHECKING,
|
|
13
14
|
Any,
|
|
14
15
|
Callable,
|
|
15
16
|
Dict,
|
|
16
17
|
Generator,
|
|
17
|
-
IO,
|
|
18
18
|
List,
|
|
19
19
|
Optional,
|
|
20
20
|
Tuple,
|
|
@@ -37,21 +37,21 @@ except ImportError:
|
|
|
37
37
|
termios = None # type: ignore[assignment]
|
|
38
38
|
|
|
39
39
|
from .exceptions import (
|
|
40
|
-
|
|
40
|
+
CommandTimedOut,
|
|
41
41
|
Failure,
|
|
42
|
+
SubprocessPipeError,
|
|
42
43
|
ThreadException,
|
|
44
|
+
UnexpectedExit,
|
|
43
45
|
WatcherError,
|
|
44
|
-
SubprocessPipeError,
|
|
45
|
-
CommandTimedOut,
|
|
46
46
|
)
|
|
47
47
|
from .terminals import (
|
|
48
48
|
WINDOWS,
|
|
49
|
-
|
|
49
|
+
bytes_to_read,
|
|
50
50
|
character_buffered,
|
|
51
|
+
pty_size,
|
|
51
52
|
ready_for_reading,
|
|
52
|
-
bytes_to_read,
|
|
53
53
|
)
|
|
54
|
-
from .util import has_fileno, isatty
|
|
54
|
+
from .util import ExceptionHandlingThread, has_fileno, isatty
|
|
55
55
|
|
|
56
56
|
if TYPE_CHECKING:
|
|
57
57
|
from .context import Context
|
|
@@ -90,7 +90,7 @@ class Runner:
|
|
|
90
90
|
minimum, this means values for each of the default
|
|
91
91
|
`.Runner.run` keyword arguments such as ``echo`` and ``warn``.
|
|
92
92
|
|
|
93
|
-
:raises
|
|
93
|
+
:raises ValueError:
|
|
94
94
|
if not all expected default values are found in ``context``.
|
|
95
95
|
"""
|
|
96
96
|
#: The `.Context` given to the same-named argument of `__init__`.
|
|
@@ -122,7 +122,7 @@ class Runner:
|
|
|
122
122
|
self._asynchronous = False
|
|
123
123
|
self._disowned = False
|
|
124
124
|
|
|
125
|
-
def run(self, command: str, **kwargs: Any) ->
|
|
125
|
+
def run(self, command: str, **kwargs: Any) -> "Result":
|
|
126
126
|
"""
|
|
127
127
|
Execute ``command``, returning an instance of `Result` once complete.
|
|
128
128
|
|
|
@@ -188,19 +188,26 @@ class Runner:
|
|
|
188
188
|
|
|
189
189
|
Specifically, ``disown=True`` has the following behaviors:
|
|
190
190
|
|
|
191
|
-
- The return value is
|
|
191
|
+
- The return value is still a `Result`, but it is severely limited
|
|
192
|
+
in functionality & data, per below.
|
|
192
193
|
- No I/O worker threads are spun up, so you will have no access to
|
|
193
|
-
the subprocess' stdout/stderr
|
|
194
|
+
the subprocess' stdout/stderr (those attributes on the result
|
|
195
|
+
will be empty strings), your stdin will not be forwarded,
|
|
194
196
|
``(out|err|in)_stream`` will be ignored, and features like
|
|
195
197
|
``watchers`` will not function.
|
|
196
198
|
- No exit code is checked for, so you will not receive any errors
|
|
197
|
-
if the subprocess fails to exit cleanly
|
|
199
|
+
if the subprocess fails to exit cleanly, the result's ``exited``
|
|
200
|
+
will be ``None``, and convenience properties like `ok` and
|
|
201
|
+
`failed` will not be reliable.
|
|
198
202
|
- ``pty=True`` may not function correctly (subprocesses may not run
|
|
199
203
|
at all; this seems to be a potential bug in Python's
|
|
200
204
|
``pty.fork``) unless your command line includes tools such as
|
|
201
205
|
``nohup`` or (the shell builtin) ``disown``.
|
|
202
206
|
|
|
203
207
|
.. versionadded:: 1.4
|
|
208
|
+
.. versionchanged:: 3.0
|
|
209
|
+
The return value when ``disown=True`` changed from `None` to
|
|
210
|
+
`Result`.
|
|
204
211
|
|
|
205
212
|
:param bool dry:
|
|
206
213
|
Whether to dry-run instead of truly invoking the given command. See
|
|
@@ -428,7 +435,7 @@ class Runner:
|
|
|
428
435
|
encoding=self.encoding,
|
|
429
436
|
)
|
|
430
437
|
|
|
431
|
-
def _run_body(self, command: str, **kwargs: Any) ->
|
|
438
|
+
def _run_body(self, command: str, **kwargs: Any) -> "Result":
|
|
432
439
|
# Prepare all the bits n bobs.
|
|
433
440
|
self._setup(command, kwargs)
|
|
434
441
|
# If dry-run, stop here.
|
|
@@ -438,10 +445,20 @@ class Runner:
|
|
|
438
445
|
)
|
|
439
446
|
# Start executing the actual command (runs in background)
|
|
440
447
|
self.start(command, self.opts["shell"], self.env)
|
|
448
|
+
# Update result data with anything only obtainable post-start.
|
|
449
|
+
self.result_kwargs["pid"] = self.get_pid()
|
|
441
450
|
# If disowned, we just stop here - no threads, no timer, no error
|
|
442
451
|
# checking, nada.
|
|
443
452
|
if self._disowned:
|
|
444
|
-
return
|
|
453
|
+
return self.generate_result(
|
|
454
|
+
**dict(
|
|
455
|
+
self.result_kwargs,
|
|
456
|
+
stdout="",
|
|
457
|
+
stderr="",
|
|
458
|
+
exited=None,
|
|
459
|
+
disowned=True,
|
|
460
|
+
)
|
|
461
|
+
)
|
|
445
462
|
# Stand up & kick off IO, timer threads
|
|
446
463
|
self.start_timer(self.opts["timeout"])
|
|
447
464
|
self.threads, self.stdout, self.stderr = self.create_io_threads()
|
|
@@ -1051,7 +1068,7 @@ class Runner:
|
|
|
1051
1068
|
execution on a remote system.
|
|
1052
1069
|
|
|
1053
1070
|
In most cases, this method will also set subclass-specific member
|
|
1054
|
-
variables used in other methods such as
|
|
1071
|
+
variables used in other methods such as `.wait` and/or `returncode`.
|
|
1055
1072
|
|
|
1056
1073
|
:param str command:
|
|
1057
1074
|
Command string to execute.
|
|
@@ -1169,9 +1186,9 @@ class Runner:
|
|
|
1169
1186
|
"""
|
|
1170
1187
|
Perform final cleanup, if necessary.
|
|
1171
1188
|
|
|
1172
|
-
This method is called within a ``finally`` clause inside the main
|
|
1173
|
-
method. Depending on the subclass, it may be a no-op, or
|
|
1174
|
-
things such as close network connections or open files.
|
|
1189
|
+
This method is called within a ``finally`` clause inside the main
|
|
1190
|
+
`~.Runner.run` method. Depending on the subclass, it may be a no-op, or
|
|
1191
|
+
it may do things such as close network connections or open files.
|
|
1175
1192
|
|
|
1176
1193
|
:returns: ``None``
|
|
1177
1194
|
|
|
@@ -1203,6 +1220,15 @@ class Runner:
|
|
|
1203
1220
|
# killed the subprocess, allowing us to even get to this point.)
|
|
1204
1221
|
return bool(self._timer and not self._timer.is_alive())
|
|
1205
1222
|
|
|
1223
|
+
def get_pid(self) -> Optional[int]:
|
|
1224
|
+
"""
|
|
1225
|
+
When possible, obtain the subprocess PID.
|
|
1226
|
+
|
|
1227
|
+
The default implementation returns None; subclasses may return useful
|
|
1228
|
+
integers.
|
|
1229
|
+
"""
|
|
1230
|
+
return None
|
|
1231
|
+
|
|
1206
1232
|
|
|
1207
1233
|
class Local(Runner):
|
|
1208
1234
|
"""
|
|
@@ -1327,12 +1353,11 @@ class Local(Runner):
|
|
|
1327
1353
|
# TODO: make subroutine?
|
|
1328
1354
|
winsize = struct.pack("HHHH", rows, cols, 0, 0)
|
|
1329
1355
|
fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, winsize)
|
|
1330
|
-
# Use
|
|
1331
|
-
# behavior.
|
|
1332
|
-
# for now.
|
|
1356
|
+
# Use execvpe for bare-minimum "exec w/ variable # args + env"
|
|
1357
|
+
# behavior.
|
|
1333
1358
|
# NOTE: stdlib subprocess (actually its posix flavor, which is
|
|
1334
1359
|
# written in C) uses either execve or execv, depending.
|
|
1335
|
-
os.
|
|
1360
|
+
os.execvpe(shell, [shell, "-c", command], env)
|
|
1336
1361
|
else:
|
|
1337
1362
|
self.process = Popen(
|
|
1338
1363
|
command,
|
|
@@ -1344,10 +1369,12 @@ class Local(Runner):
|
|
|
1344
1369
|
stdin=PIPE,
|
|
1345
1370
|
)
|
|
1346
1371
|
|
|
1372
|
+
def get_pid(self) -> int:
|
|
1373
|
+
return self.pid if self.using_pty else self.process.pid
|
|
1374
|
+
|
|
1347
1375
|
def kill(self) -> None:
|
|
1348
|
-
pid = self.pid if self.using_pty else self.process.pid
|
|
1349
1376
|
try:
|
|
1350
|
-
os.kill(
|
|
1377
|
+
os.kill(self.get_pid(), signal.SIGKILL)
|
|
1351
1378
|
except ProcessLookupError:
|
|
1352
1379
|
# In odd situations where our subprocess is already dead, don't
|
|
1353
1380
|
# throw this upwards.
|
|
@@ -1453,6 +1480,21 @@ class Result:
|
|
|
1453
1480
|
results in ``result.hide == ('stdout', 'stderr')``; and ``hide=False``
|
|
1454
1481
|
(the default) generates ``result.hide == ()`` (the empty tuple.)
|
|
1455
1482
|
|
|
1483
|
+
:param int pid:
|
|
1484
|
+
The process ID of the subprocess that was executed (normally) or is
|
|
1485
|
+
executing (when asynchronous or disowned).
|
|
1486
|
+
|
|
1487
|
+
.. versionadded:: 3.0
|
|
1488
|
+
|
|
1489
|
+
:param bool disowned:
|
|
1490
|
+
Whether the subprocess was 'disowned' or detached from ourselves; in
|
|
1491
|
+
this mode may other attributes or methods of this `Result` will be
|
|
1492
|
+
unreliable, see `Runner.run` docs for details. You may want to use your
|
|
1493
|
+
own methods to interrogate your operating system about this object's
|
|
1494
|
+
``pid``, however, which will usually be valid.
|
|
1495
|
+
|
|
1496
|
+
.. versionadded:: 3.0
|
|
1497
|
+
|
|
1456
1498
|
.. note::
|
|
1457
1499
|
`Result` objects' truth evaluation is equivalent to their `.ok`
|
|
1458
1500
|
attribute's value. Therefore, quick-and-dirty expressions like the
|
|
@@ -1481,6 +1523,8 @@ class Result:
|
|
|
1481
1523
|
exited: int = 0,
|
|
1482
1524
|
pty: bool = False,
|
|
1483
1525
|
hide: Tuple[str, ...] = tuple(),
|
|
1526
|
+
pid: Optional[int] = None,
|
|
1527
|
+
disowned: bool = False,
|
|
1484
1528
|
):
|
|
1485
1529
|
self.stdout = stdout
|
|
1486
1530
|
self.stderr = stderr
|
|
@@ -1493,6 +1537,8 @@ class Result:
|
|
|
1493
1537
|
self.exited = exited
|
|
1494
1538
|
self.pty = pty
|
|
1495
1539
|
self.hide = hide
|
|
1540
|
+
self.pid = pid
|
|
1541
|
+
self.disowned = disowned
|
|
1496
1542
|
|
|
1497
1543
|
@property
|
|
1498
1544
|
def return_code(self) -> int:
|
|
@@ -1515,11 +1561,9 @@ class Result:
|
|
|
1515
1561
|
for x in ("stdout", "stderr"):
|
|
1516
1562
|
val = getattr(self, x)
|
|
1517
1563
|
ret.append(
|
|
1518
|
-
"""=== {} ===
|
|
1519
|
-
{}
|
|
1520
|
-
"""
|
|
1521
|
-
x, val.rstrip()
|
|
1522
|
-
)
|
|
1564
|
+
f"""=== {x} ===
|
|
1565
|
+
{val.rstrip()}
|
|
1566
|
+
"""
|
|
1523
1567
|
if val
|
|
1524
1568
|
else "(no {})".format(x)
|
|
1525
1569
|
)
|