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.
Files changed (192) hide show
  1. {invoke-2.2.1 → invoke-3.0.0}/MANIFEST.in +1 -1
  2. {invoke-2.2.1/invoke.egg-info → invoke-3.0.0}/PKG-INFO +18 -21
  3. {invoke-2.2.1 → invoke-3.0.0}/README.rst +8 -4
  4. {invoke-2.2.1 → invoke-3.0.0}/invoke/__init__.py +9 -7
  5. {invoke-2.2.1 → invoke-3.0.0}/invoke/config.py +1 -1
  6. {invoke-2.2.1 → invoke-3.0.0}/invoke/context.py +49 -35
  7. {invoke-2.2.1 → invoke-3.0.0}/invoke/executor.py +8 -5
  8. {invoke-2.2.1 → invoke-3.0.0}/invoke/loader.py +1 -1
  9. {invoke-2.2.1 → invoke-3.0.0}/invoke/program.py +2 -2
  10. {invoke-2.2.1 → invoke-3.0.0}/invoke/runners.py +75 -31
  11. {invoke-2.2.1 → invoke-3.0.0}/invoke/tasks.py +15 -9
  12. {invoke-2.2.1 → invoke-3.0.0/invoke.egg-info}/PKG-INFO +18 -21
  13. {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/SOURCES.txt +0 -3
  14. invoke-3.0.0/pyproject.toml +108 -0
  15. invoke-3.0.0/sites/docs/.readthedocs.yaml +14 -0
  16. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/invoking-tasks.rst +54 -0
  17. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/conf.py +12 -1
  18. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/invoke.rst +15 -1
  19. {invoke-2.2.1 → invoke-3.0.0}/sites/shared_conf.py +2 -3
  20. invoke-3.0.0/sites/www/.readthedocs.yaml +14 -0
  21. {invoke-2.2.1 → invoke-3.0.0}/sites/www/changelog.rst +77 -6
  22. invoke-3.0.0/sites/www/contact.rst +11 -0
  23. {invoke-2.2.1 → invoke-3.0.0}/sites/www/development.rst +8 -5
  24. {invoke-2.2.1 → invoke-3.0.0}/sites/www/index.rst +5 -5
  25. {invoke-2.2.1 → invoke-3.0.0}/sites/www/installing.rst +6 -7
  26. {invoke-2.2.1 → invoke-3.0.0}/tasks.py +50 -20
  27. {invoke-2.2.1 → invoke-3.0.0}/tests/_util.py +1 -1
  28. {invoke-2.2.1 → invoke-3.0.0}/tests/collection.py +1 -0
  29. {invoke-2.2.1 → invoke-3.0.0}/tests/config.py +2 -1
  30. {invoke-2.2.1 → invoke-3.0.0}/tests/context.py +10 -8
  31. {invoke-2.2.1 → invoke-3.0.0}/tests/executor.py +4 -5
  32. {invoke-2.2.1 → invoke-3.0.0}/tests/init.py +1 -14
  33. {invoke-2.2.1 → invoke-3.0.0}/tests/parser_context.py +1 -0
  34. {invoke-2.2.1 → invoke-3.0.0}/tests/parser_parser.py +78 -72
  35. {invoke-2.2.1 → invoke-3.0.0}/tests/program.py +24 -26
  36. {invoke-2.2.1 → invoke-3.0.0}/tests/runners.py +45 -16
  37. {invoke-2.2.1 → invoke-3.0.0}/tests/task.py +19 -7
  38. {invoke-2.2.1 → invoke-3.0.0}/tests/util.py +2 -0
  39. {invoke-2.2.1 → invoke-3.0.0}/tests/watchers.py +1 -0
  40. invoke-2.2.1/dev-requirements.txt +0 -23
  41. invoke-2.2.1/invoke/_version.py +0 -2
  42. invoke-2.2.1/pyproject.toml +0 -52
  43. invoke-2.2.1/setup.py +0 -78
  44. invoke-2.2.1/sites/docs/.readthedocs.yaml +0 -13
  45. invoke-2.2.1/sites/www/.readthedocs.yaml +0 -13
  46. invoke-2.2.1/sites/www/contact.rst +0 -25
  47. {invoke-2.2.1 → invoke-3.0.0}/LICENSE +0 -0
  48. {invoke-2.2.1 → invoke-3.0.0}/invoke/__main__.py +0 -0
  49. {invoke-2.2.1 → invoke-3.0.0}/invoke/collection.py +0 -0
  50. {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/__init__.py +0 -0
  51. {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/bash.completion +0 -0
  52. {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/complete.py +0 -0
  53. {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/fish.completion +0 -0
  54. {invoke-2.2.1 → invoke-3.0.0}/invoke/completion/zsh.completion +0 -0
  55. {invoke-2.2.1 → invoke-3.0.0}/invoke/env.py +0 -0
  56. {invoke-2.2.1 → invoke-3.0.0}/invoke/exceptions.py +0 -0
  57. {invoke-2.2.1 → invoke-3.0.0}/invoke/main.py +0 -0
  58. {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/__init__.py +0 -0
  59. {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/argument.py +0 -0
  60. {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/context.py +0 -0
  61. {invoke-2.2.1 → invoke-3.0.0}/invoke/parser/parser.py +0 -0
  62. {invoke-2.2.1 → invoke-3.0.0}/invoke/py.typed +0 -0
  63. {invoke-2.2.1 → invoke-3.0.0}/invoke/terminals.py +0 -0
  64. {invoke-2.2.1 → invoke-3.0.0}/invoke/util.py +0 -0
  65. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/__init__.py +0 -0
  66. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/__init__.py +0 -0
  67. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/backwardscompat.py +0 -0
  68. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/fluidity/machine.py +0 -0
  69. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/__init__.py +0 -0
  70. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/_version.py +0 -0
  71. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/alias_dict.py +0 -0
  72. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/lexicon/attribute_dict.py +0 -0
  73. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/__init__.py +0 -0
  74. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/composer.py +0 -0
  75. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/constructor.py +0 -0
  76. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/cyaml.py +0 -0
  77. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/dumper.py +0 -0
  78. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/emitter.py +0 -0
  79. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/error.py +0 -0
  80. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/events.py +0 -0
  81. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/loader.py +0 -0
  82. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/nodes.py +0 -0
  83. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/parser.py +0 -0
  84. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/reader.py +0 -0
  85. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/representer.py +0 -0
  86. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/resolver.py +0 -0
  87. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/scanner.py +0 -0
  88. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/serializer.py +0 -0
  89. {invoke-2.2.1 → invoke-3.0.0}/invoke/vendor/yaml/tokens.py +0 -0
  90. {invoke-2.2.1 → invoke-3.0.0}/invoke/watchers.py +0 -0
  91. {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/dependency_links.txt +0 -0
  92. {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/entry_points.txt +0 -0
  93. {invoke-2.2.1 → invoke-3.0.0}/invoke.egg-info/top_level.txt +0 -0
  94. {invoke-2.2.1 → invoke-3.0.0}/setup.cfg +0 -0
  95. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/_static/rtd.css +0 -0
  96. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/__init__.rst +0 -0
  97. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/collection.rst +0 -0
  98. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/config.rst +0 -0
  99. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/context.rst +0 -0
  100. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/exceptions.rst +0 -0
  101. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/executor.rst +0 -0
  102. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/loader.rst +0 -0
  103. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/parser.rst +0 -0
  104. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/program.rst +0 -0
  105. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/runners.rst +0 -0
  106. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/tasks.rst +0 -0
  107. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/terminals.rst +0 -0
  108. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/util.rst +0 -0
  109. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/api/watchers.rst +0 -0
  110. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/configuration.rst +0 -0
  111. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/library.rst +0 -0
  112. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/loading.rst +0 -0
  113. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/namespaces.rst +0 -0
  114. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/testing.rst +0 -0
  115. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/concepts/watchers.rst +0 -0
  116. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/getting-started.rst +0 -0
  117. {invoke-2.2.1 → invoke-3.0.0}/sites/docs/index.rst +0 -0
  118. {invoke-2.2.1 → invoke-3.0.0}/sites/www/conf.py +0 -0
  119. {invoke-2.2.1 → invoke-3.0.0}/sites/www/faq.rst +0 -0
  120. {invoke-2.2.1 → invoke-3.0.0}/sites/www/prior-art.rst +0 -0
  121. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/alias_sorting.py +0 -0
  122. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/autoprint.py +0 -0
  123. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/branch/explicit.py +0 -0
  124. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/branch/tasks.py +0 -0
  125. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.json +0 -0
  126. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.py +0 -0
  127. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.yaml +0 -0
  128. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/all-four/invoke.yml +0 -0
  129. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/collection.py +0 -0
  130. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/echo.yaml +0 -0
  131. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json/invoke.json +0 -0
  132. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json-and-python/invoke.json +0 -0
  133. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/json-and-python/invoke.py +0 -0
  134. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/nested/invoke.yaml +0 -0
  135. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/no-dedupe.yaml +0 -0
  136. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/no-echo.yaml +0 -0
  137. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/package/invoke.yml +0 -0
  138. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/package/tasks/__init__.py +0 -0
  139. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/python/invoke.py +0 -0
  140. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/runtime.py +0 -0
  141. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.json +0 -0
  142. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.py +0 -0
  143. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/three-of-em/invoke.yml +0 -0
  144. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/underscores/invoke.yaml +0 -0
  145. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/underscores/tasks.py +0 -0
  146. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/explicit.py +0 -0
  147. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/invoke.yaml +0 -0
  148. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yaml/tasks.py +0 -0
  149. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/explicit.py +0 -0
  150. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/invoke.yml +0 -0
  151. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/configs/yml/tasks.py +0 -0
  152. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/contextualized.py +0 -0
  153. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/custom_executor.py +0 -0
  154. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/debugging.py +0 -0
  155. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/decorator_multi_default.py +0 -0
  156. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/decorators.py +0 -0
  157. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/deeper_ns_list.py +0 -0
  158. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/depth_first.py +0 -0
  159. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/docstrings.py +0 -0
  160. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/empty.py +0 -0
  161. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/empty_subcollection.py +0 -0
  162. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/explicit_root.py +0 -0
  163. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/foo.py +0 -0
  164. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/has_modules.py +0 -0
  165. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/ignoreme/ignoremetoo/.no +0 -0
  166. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/integration.py +0 -0
  167. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/namespacing.py +0 -0
  168. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/nontrivial_docstrings.py +0 -0
  169. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/oops.py +0 -0
  170. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/package/__init__.py +0 -0
  171. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/package/module.py +0 -0
  172. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/simple_ns_list.py +0 -0
  173. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subcollection_task_name.py +0 -0
  174. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subspace/__init__.py +0 -0
  175. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/subspace/module.py +0 -0
  176. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/sudo_prompt.py +0 -0
  177. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tasks.py +0 -0
  178. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/__init__.py +0 -0
  179. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/__init__.py +0 -0
  180. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/docs.py +0 -0
  181. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/build/python.py +0 -0
  182. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/deploy.py +0 -0
  183. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree/provision.py +0 -0
  184. {invoke-2.2.1 → invoke-3.0.0}/tests/_support/tree.json +0 -0
  185. {invoke-2.2.1 → invoke-3.0.0}/tests/cli.py +0 -0
  186. {invoke-2.2.1 → invoke-3.0.0}/tests/completion.py +0 -0
  187. {invoke-2.2.1 → invoke-3.0.0}/tests/concurrency.py +0 -0
  188. {invoke-2.2.1 → invoke-3.0.0}/tests/conftest.py +0 -0
  189. {invoke-2.2.1 → invoke-3.0.0}/tests/loader.py +0 -0
  190. {invoke-2.2.1 → invoke-3.0.0}/tests/merge_dicts.py +0 -0
  191. {invoke-2.2.1 → invoke-3.0.0}/tests/parser_argument.py +0 -0
  192. {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
1
+ Metadata-Version: 2.4
2
2
  Name: invoke
3
- Version: 2.2.1
3
+ Version: 3.0.0
4
4
  Summary: Pythonic task execution
5
- Home-page: https://pyinvoke.org
6
- Author: Jeff Forcier
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.6
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 (2.7 and 3.4+) library for managing shell-oriented
64
- subprocesses and organizing executable Python code into CLI-invokable tasks. It
65
- draws inspiration from various sources (``make``/``rake``, Fabric 1.x, etc) to
66
- arrive at a powerful & clean feature set.
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 (2.7 and 3.4+) library for managing shell-oriented
23
- subprocesses and organizing executable Python code into CLI-invokable tasks. It
24
- draws inspiration from various sources (``make``/``rake``, Fabric 1.x, etc) to
25
- arrive at a powerful & clean feature set.
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 typing import Any, Optional
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 Runner, Local, Failure, Result, Promise # noqa
29
- from .tasks import task, call, Call, Task # noqa
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) -> Optional[Result]:
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) -> Optional[Result]:
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 = "/bin/bash"
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 Failure, AuthFailure, ResponseNotAccepted
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
- def __init__(self, config: Optional[Config] = None) -> None:
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(_config=config)
64
- #: A list of commands to run (via "&&") before the main argument to any
65
- #: `run` or `sudo` calls. Note that the primary API for manipulating
66
- #: this list is `prefix`; see its docs for details.
67
- command_prefixes: List[str] = list()
68
- self._set(command_prefixes=command_prefixes)
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) -> Optional[Result]:
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) -> Optional[Result]:
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 .parser import Parser, ParserContext, Argument
22
- from .exceptions import UnexpectedExit, CollectionNotFound, ParseError, Exit
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 signal
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
- UnexpectedExit,
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
- pty_size,
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, ExceptionHandlingThread
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 exceptions.ValueError:
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) -> Optional["Result"]:
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 ``None`` instead of a `Result` or subclass.
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, your stdin will not be forwarded,
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) -> Optional["Result"]:
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 None
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 `wait` and/or `returncode`.
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 `run`
1173
- method. Depending on the subclass, it may be a no-op, or it may do
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 execve for bare-minimum "exec w/ variable # args + env"
1331
- # behavior. No need for the 'p' (use PATH to find executable)
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.execve(shell, [shell, "-c", command], env)
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(pid, signal.SIGKILL)
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
- """.format(
1521
- x, val.rstrip()
1522
- )
1564
+ f"""=== {x} ===
1565
+ {val.rstrip()}
1566
+ """
1523
1567
  if val
1524
1568
  else "(no {})".format(x)
1525
1569
  )