pyinfra 2.9.1__py2.py3-none-any.whl → 3.0__py2.py3-none-any.whl

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 (156) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +265 -253
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +68 -53
  5. pyinfra/api/config.py +139 -32
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +7 -26
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +102 -137
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +21 -25
  13. pyinfra/api/operation.py +240 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +79 -86
  17. pyinfra/connectors/base.py +147 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +220 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +196 -208
  22. pyinfra/connectors/ssh.py +530 -613
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +211 -137
  27. pyinfra/connectors/vagrant.py +60 -53
  28. pyinfra/context.py +4 -2
  29. pyinfra/facts/apk.py +2 -0
  30. pyinfra/facts/apt.py +2 -0
  31. pyinfra/facts/brew.py +2 -0
  32. pyinfra/facts/bsdinit.py +2 -0
  33. pyinfra/facts/cargo.py +2 -0
  34. pyinfra/facts/choco.py +2 -0
  35. pyinfra/facts/deb.py +7 -2
  36. pyinfra/facts/dnf.py +2 -0
  37. pyinfra/facts/docker.py +19 -0
  38. pyinfra/facts/files.py +47 -32
  39. pyinfra/facts/gem.py +2 -0
  40. pyinfra/facts/git.py +3 -1
  41. pyinfra/facts/gpg.py +3 -1
  42. pyinfra/facts/hardware.py +34 -24
  43. pyinfra/facts/iptables.py +5 -3
  44. pyinfra/facts/launchd.py +2 -0
  45. pyinfra/facts/lxd.py +2 -0
  46. pyinfra/facts/mysql.py +13 -6
  47. pyinfra/facts/npm.py +1 -0
  48. pyinfra/facts/openrc.py +2 -0
  49. pyinfra/facts/pacman.py +6 -2
  50. pyinfra/facts/pip.py +2 -0
  51. pyinfra/facts/pkg.py +2 -0
  52. pyinfra/facts/pkgin.py +2 -0
  53. pyinfra/facts/postgres.py +168 -0
  54. pyinfra/facts/postgresql.py +6 -160
  55. pyinfra/facts/rpm.py +12 -9
  56. pyinfra/facts/runit.py +68 -0
  57. pyinfra/facts/selinux.py +3 -1
  58. pyinfra/facts/server.py +80 -36
  59. pyinfra/facts/snap.py +2 -0
  60. pyinfra/facts/systemd.py +31 -12
  61. pyinfra/facts/sysvinit.py +10 -10
  62. pyinfra/facts/upstart.py +2 -0
  63. pyinfra/facts/util/packaging.py +7 -4
  64. pyinfra/facts/vzctl.py +2 -0
  65. pyinfra/facts/xbps.py +2 -0
  66. pyinfra/facts/yum.py +2 -0
  67. pyinfra/facts/zypper.py +2 -0
  68. pyinfra/local.py +4 -5
  69. pyinfra/operations/apk.py +6 -4
  70. pyinfra/operations/apt.py +46 -65
  71. pyinfra/operations/brew.py +17 -22
  72. pyinfra/operations/bsdinit.py +9 -7
  73. pyinfra/operations/cargo.py +4 -2
  74. pyinfra/operations/choco.py +4 -2
  75. pyinfra/operations/dnf.py +19 -23
  76. pyinfra/operations/docker.py +339 -0
  77. pyinfra/operations/files.py +188 -386
  78. pyinfra/operations/gem.py +4 -2
  79. pyinfra/operations/git.py +24 -53
  80. pyinfra/operations/iptables.py +29 -35
  81. pyinfra/operations/launchd.py +6 -7
  82. pyinfra/operations/lxd.py +8 -13
  83. pyinfra/operations/mysql.py +62 -81
  84. pyinfra/operations/npm.py +9 -2
  85. pyinfra/operations/openrc.py +6 -4
  86. pyinfra/operations/pacman.py +7 -8
  87. pyinfra/operations/pip.py +25 -24
  88. pyinfra/operations/pkg.py +4 -2
  89. pyinfra/operations/pkgin.py +6 -4
  90. pyinfra/operations/postgres.py +349 -0
  91. pyinfra/operations/postgresql.py +18 -379
  92. pyinfra/operations/puppet.py +3 -1
  93. pyinfra/operations/python.py +8 -19
  94. pyinfra/operations/runit.py +182 -0
  95. pyinfra/operations/selinux.py +47 -44
  96. pyinfra/operations/server.py +111 -127
  97. pyinfra/operations/snap.py +4 -4
  98. pyinfra/operations/ssh.py +20 -33
  99. pyinfra/operations/systemd.py +19 -15
  100. pyinfra/operations/sysvinit.py +9 -16
  101. pyinfra/operations/upstart.py +9 -7
  102. pyinfra/operations/util/__init__.py +12 -0
  103. pyinfra/operations/util/docker.py +177 -0
  104. pyinfra/operations/util/files.py +24 -16
  105. pyinfra/operations/util/packaging.py +55 -57
  106. pyinfra/operations/util/service.py +39 -51
  107. pyinfra/operations/vzctl.py +12 -10
  108. pyinfra/operations/xbps.py +6 -4
  109. pyinfra/operations/yum.py +18 -22
  110. pyinfra/operations/zypper.py +12 -13
  111. pyinfra/version.py +5 -2
  112. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
  113. pyinfra-3.0.dist-info/RECORD +167 -0
  114. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
  115. pyinfra-3.0.dist-info/entry_points.txt +11 -0
  116. pyinfra_cli/__main__.py +4 -3
  117. pyinfra_cli/commands.py +7 -2
  118. pyinfra_cli/exceptions.py +78 -42
  119. pyinfra_cli/inventory.py +40 -6
  120. pyinfra_cli/log.py +17 -3
  121. pyinfra_cli/main.py +133 -90
  122. pyinfra_cli/prints.py +95 -127
  123. pyinfra_cli/util.py +62 -29
  124. tests/test_api/test_api.py +2 -0
  125. tests/test_api/test_api_arguments.py +13 -13
  126. tests/test_api/test_api_deploys.py +28 -29
  127. tests/test_api/test_api_facts.py +60 -98
  128. tests/test_api/test_api_operations.py +101 -201
  129. tests/test_cli/test_cli.py +18 -49
  130. tests/test_cli/test_cli_deploy.py +11 -37
  131. tests/test_cli/test_cli_exceptions.py +50 -19
  132. tests/test_cli/util.py +1 -1
  133. tests/test_connectors/test_chroot.py +6 -6
  134. tests/test_connectors/test_docker.py +4 -4
  135. tests/test_connectors/test_dockerssh.py +38 -50
  136. tests/test_connectors/test_local.py +11 -12
  137. tests/test_connectors/test_ssh.py +105 -93
  138. tests/test_connectors/test_terraform.py +9 -15
  139. tests/test_connectors/test_util.py +24 -46
  140. tests/test_connectors/test_vagrant.py +7 -7
  141. pyinfra/api/operation.pyi +0 -117
  142. pyinfra/connectors/ansible.py +0 -171
  143. pyinfra/connectors/mech.py +0 -186
  144. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  145. pyinfra/connectors/winrm.py +0 -320
  146. pyinfra/facts/windows.py +0 -366
  147. pyinfra/facts/windows_files.py +0 -90
  148. pyinfra/operations/windows.py +0 -59
  149. pyinfra/operations/windows_files.py +0 -551
  150. pyinfra-2.9.1.dist-info/RECORD +0 -170
  151. pyinfra-2.9.1.dist-info/entry_points.txt +0 -14
  152. tests/test_connectors/test_ansible.py +0 -64
  153. tests/test_connectors/test_mech.py +0 -126
  154. tests/test_connectors/test_winrm.py +0 -76
  155. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
  156. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 2.9.1
3
+ Version: 3.0
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -16,14 +16,15 @@ Classifier: Intended Audience :: Information Technology
16
16
  Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.6
20
- Classifier: Programming Language :: Python :: 3.7
21
19
  Classifier: Programming Language :: Python :: 3.8
22
20
  Classifier: Programming Language :: Python :: 3.9
23
21
  Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Topic :: System :: Systems Administration
25
25
  Classifier: Topic :: System :: Installation/Setup
26
26
  Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.8
27
28
  Description-Content-Type: text/markdown
28
29
  License-File: LICENSE.md
29
30
  Requires-Dist: gevent >=1.5
@@ -34,25 +35,28 @@ Requires-Dist: python-dateutil <3,>2
34
35
  Requires-Dist: setuptools
35
36
  Requires-Dist: configparser
36
37
  Requires-Dist: pywinrm
38
+ Requires-Dist: typeguard
37
39
  Requires-Dist: distro <2,>=1.6
40
+ Requires-Dist: packaging >=16.1
41
+ Requires-Dist: importlib-metadata >=3.6 ; python_version < "3.10"
42
+ Requires-Dist: typing-extensions ; python_version < "3.11"
38
43
  Requires-Dist: graphlib-backport ; python_version < "3.9"
39
- Provides-Extra: ansible
40
- Requires-Dist: pyyaml ; extra == 'ansible'
41
44
  Provides-Extra: dev
42
- Requires-Dist: pyyaml ; extra == 'dev'
43
- Requires-Dist: pytest-cov ==4.0.0 ; extra == 'dev'
44
- Requires-Dist: black ==22.3.0 ; extra == 'dev'
45
- Requires-Dist: isort ==5.10.1 ; extra == 'dev'
46
- Requires-Dist: flake8 ==4.0.1 ; extra == 'dev'
47
- Requires-Dist: flake8-black ==0.3.0 ; extra == 'dev'
48
- Requires-Dist: flake8-isort ==4.1.1 ; extra == 'dev'
49
- Requires-Dist: mypy ==0.971 ; extra == 'dev'
45
+ Requires-Dist: pytest ==8.2.1 ; extra == 'dev'
46
+ Requires-Dist: coverage ==7.5.1 ; extra == 'dev'
47
+ Requires-Dist: pytest-cov ==5.0.0 ; extra == 'dev'
48
+ Requires-Dist: black ==24.4.2 ; extra == 'dev'
49
+ Requires-Dist: isort ==5.13.2 ; extra == 'dev'
50
+ Requires-Dist: flake8 ==7.0.0 ; extra == 'dev'
51
+ Requires-Dist: flake8-black ==0.3.6 ; extra == 'dev'
52
+ Requires-Dist: flake8-isort ==6.1.1 ; extra == 'dev'
53
+ Requires-Dist: mypy ; extra == 'dev'
50
54
  Requires-Dist: types-cryptography ; extra == 'dev'
51
55
  Requires-Dist: types-paramiko ; extra == 'dev'
52
56
  Requires-Dist: types-python-dateutil ; extra == 'dev'
53
57
  Requires-Dist: types-PyYAML ; extra == 'dev'
54
58
  Requires-Dist: types-setuptools ; extra == 'dev'
55
- Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.14 ; extra == 'dev'
59
+ Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'dev'
56
60
  Requires-Dist: myst-parser ==2.0.0 ; extra == 'dev'
57
61
  Requires-Dist: sphinx ==6.2.1 ; extra == 'dev'
58
62
  Requires-Dist: wheel ; extra == 'dev'
@@ -62,32 +66,25 @@ Requires-Dist: ipdb ; extra == 'dev'
62
66
  Requires-Dist: ipdbplugin ; extra == 'dev'
63
67
  Requires-Dist: flake8-spellcheck ==0.12.1 ; extra == 'dev'
64
68
  Requires-Dist: redbaron ; extra == 'dev'
65
- Requires-Dist: pytest ==7.0.1 ; (python_version <= "3.6") and extra == 'dev'
66
- Requires-Dist: coverage ==6.2 ; (python_version <= "3.6") and extra == 'dev'
67
- Requires-Dist: pytest ==7.2.0 ; (python_version > "3.6") and extra == 'dev'
68
- Requires-Dist: coverage ==6.5 ; (python_version > "3.6") and extra == 'dev'
69
69
  Provides-Extra: docs
70
- Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.14 ; extra == 'docs'
70
+ Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'docs'
71
71
  Requires-Dist: myst-parser ==2.0.0 ; extra == 'docs'
72
72
  Requires-Dist: sphinx ==6.2.1 ; extra == 'docs'
73
73
  Provides-Extra: test
74
- Requires-Dist: pyyaml ; extra == 'test'
75
- Requires-Dist: pytest-cov ==4.0.0 ; extra == 'test'
76
- Requires-Dist: black ==22.3.0 ; extra == 'test'
77
- Requires-Dist: isort ==5.10.1 ; extra == 'test'
78
- Requires-Dist: flake8 ==4.0.1 ; extra == 'test'
79
- Requires-Dist: flake8-black ==0.3.0 ; extra == 'test'
80
- Requires-Dist: flake8-isort ==4.1.1 ; extra == 'test'
81
- Requires-Dist: mypy ==0.971 ; extra == 'test'
74
+ Requires-Dist: pytest ==8.2.1 ; extra == 'test'
75
+ Requires-Dist: coverage ==7.5.1 ; extra == 'test'
76
+ Requires-Dist: pytest-cov ==5.0.0 ; extra == 'test'
77
+ Requires-Dist: black ==24.4.2 ; extra == 'test'
78
+ Requires-Dist: isort ==5.13.2 ; extra == 'test'
79
+ Requires-Dist: flake8 ==7.0.0 ; extra == 'test'
80
+ Requires-Dist: flake8-black ==0.3.6 ; extra == 'test'
81
+ Requires-Dist: flake8-isort ==6.1.1 ; extra == 'test'
82
+ Requires-Dist: mypy ; extra == 'test'
82
83
  Requires-Dist: types-cryptography ; extra == 'test'
83
84
  Requires-Dist: types-paramiko ; extra == 'test'
84
85
  Requires-Dist: types-python-dateutil ; extra == 'test'
85
86
  Requires-Dist: types-PyYAML ; extra == 'test'
86
87
  Requires-Dist: types-setuptools ; extra == 'test'
87
- Requires-Dist: pytest ==7.0.1 ; (python_version <= "3.6") and extra == 'test'
88
- Requires-Dist: coverage ==6.2 ; (python_version <= "3.6") and extra == 'test'
89
- Requires-Dist: pytest ==7.2.0 ; (python_version > "3.6") and extra == 'test'
90
- Requires-Dist: coverage ==6.5 ; (python_version > "3.6") and extra == 'test'
91
88
 
92
89
  <p align="center">
93
90
  <a href="https://pyinfra.com">
@@ -95,25 +92,27 @@ Requires-Dist: coverage ==6.5 ; (python_version > "3.6") and extra == 'test'
95
92
  </a>
96
93
  </p>
97
94
 
98
- <p align="center">
99
- <em>pyinfra automates infrastructure using Python. It’s fast and scales from one server to thousands. Great for ad-hoc command execution, service deployment, configuration management and more.</em>
95
+ <p>
96
+ <strong>Note: this is the v3 branch, which is currently in beta. <a href="https://docs.pyinfra.com/en/next">See the docs for v3</a>. If needed the <a href="https://github.com/pyinfra-dev/pyinfra/tree/2.x/">2.x branch is here</a>, but is in bugfix only mode.</strong>
97
+ </p>
98
+
99
+ <p>
100
+ pyinfra turns Python code into shell commands and runs them on your servers. Execute ad-hoc commands and write declarative operations. Target SSH servers, local machine and Docker containers. Fast and scales from one server to thousands. Think <code>ansible</code> but Python instead of YAML, and a lot faster.
100
101
  </p>
101
102
 
102
103
  ---
103
104
 
104
- <p align="center">
105
- <a href="https://docs.pyinfra.com"><strong>Documentation</strong></a> &rArr;
105
+ <h3>
106
106
  <a href="https://docs.pyinfra.com/page/getting-started.html"><strong>Getting Started</strong></a> &bull;
107
- <a href="https://docs.pyinfra.com/page/examples.html"><strong>Examples</strong></a> &bull;
107
+ <a href="https://github.com/pyinfra-dev/pyinfra-examples"><strong>Examples Repo</strong></a> &bull;
108
+ <a href="https://matrix.to/#/#pyinfra:matrix.org"><strong>Chat on Matrix</strong></a>
109
+ </h3>
110
+ <p>
111
+ <a href="https://docs.pyinfra.com"><strong>Documentation</strong></a> &bull;
108
112
  <a href="https://docs.pyinfra.com/page/support.html"><strong>Help & Support</strong></a> &bull;
109
113
  <a href="https://docs.pyinfra.com/page/contributing.html"><strong>Contributing</strong></a>
110
114
  </p>
111
115
 
112
- <p align="center">
113
- Chat &rArr;
114
- <a href="https://matrix.to/#/#pyinfra:matrix.org"><strong><code>#pyinfra</code> on Matrix</strong></a>
115
- </p>
116
-
117
116
  ---
118
117
 
119
118
  Why pyinfra? Design features include:
@@ -0,0 +1,167 @@
1
+ pyinfra/__init__.py,sha256=7ZcKHGWk7_nYxsYrbFBB_vJr-J-Ddbc56ZS4sk5ArVw,535
2
+ pyinfra/__main__.py,sha256=aVd00glLz5CMJGXgt1XxbOvC2HluqaowoTOjxgIpBaA,47
3
+ pyinfra/context.py,sha256=S6DvGjjTEjM4u2m9oIAmAaV7kXIJzVwYf725P1muIuY,3395
4
+ pyinfra/local.py,sha256=0bpIRCyDKM6i_jA1i8Ej2qr_iWIF9cUYWutXNdLj8po,2751
5
+ pyinfra/progress.py,sha256=X3hXZ4Flh_L9FE4ZEWxWoG0R4dA5UPd1FCO-Exd5Xtc,4193
6
+ pyinfra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ pyinfra/version.py,sha256=LZf50PHDzEZv65w0G-iMICoQ9US0U5LWHAOEmNtkF3I,216
8
+ pyinfra/api/__init__.py,sha256=suGbKKM-qCduuXFYBEcyswlTqozewtYpdLRhK63PVn0,942
9
+ pyinfra/api/arguments.py,sha256=8BhuJiljFJzaCi9lM8HoEBB9mcnCBilv4Zwdx_349g8,9941
10
+ pyinfra/api/arguments_typed.py,sha256=WQKr0wDtlgJGq-Vkv_oPAz7f-LxjqQv3wJCdvVrePWk,2331
11
+ pyinfra/api/command.py,sha256=SyUlxvhYlXpgFpg0jua8bzQ2KPtVYQXHcvD6AUL2SCI,7226
12
+ pyinfra/api/config.py,sha256=wS6Pi4T4DxNkzO4llNY-ghLxyI5VBJ26uGvgMPZxIKY,9043
13
+ pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
14
+ pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
15
+ pyinfra/api/deploy.py,sha256=xo4F7URUf3xzIChRHZn4zwqs_WTjLjZNC9i9eQjAFk8,2756
16
+ pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
17
+ pyinfra/api/facts.py,sha256=L_Wsrdq-u0zR69klnXWZ_2j9H7m3Oto53X0bQf670X8,10574
18
+ pyinfra/api/host.py,sha256=3lRhlZDRKvCNvpziaTglExy2Ep1wd4YdmGDNY4emAdA,13466
19
+ pyinfra/api/inventory.py,sha256=nPITdNEJ7q71adIqS_OKHsMjD7amUuHEuTl6xzgh1Gk,7734
20
+ pyinfra/api/operation.py,sha256=pB0LpjUqbMLGtoDnsckNw0FRoeo1BopL0fnXVDM1JyU,15112
21
+ pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
22
+ pyinfra/api/state.py,sha256=3dXRjeZJXnzLcbP9E4aogkRPwIg3_kK1h4Tf4FVZock,12622
23
+ pyinfra/api/util.py,sha256=vesFOxExETb-B-oAhWpJJrmALTubg3fkFqi0gYd0pJs,12265
24
+ pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ pyinfra/connectors/base.py,sha256=2fASiV-MvpXgcZAFLM_PUwYx5ax6EHai44ri_oEKeSE,3732
26
+ pyinfra/connectors/chroot.py,sha256=Xd72I8T58KIwKOoc0LXCw91AoEIaiHfRLDcDVTHGJ0o,5931
27
+ pyinfra/connectors/docker.py,sha256=2UNHhXS4hpLo7I19ixDeSd7JR8SNo43VgqsaUIZQZJ4,8741
28
+ pyinfra/connectors/dockerssh.py,sha256=VWHY--jqs3yf-RuPUZXav4vLeON9SzoVC9CUyOJo1rg,8919
29
+ pyinfra/connectors/local.py,sha256=vYOBQS_5rf-dVaPeG4dJlLwBHqkxAzLjj3aDEgbAsx8,6900
30
+ pyinfra/connectors/ssh.py,sha256=QvXrmg3G47VwGEiPP-8Nse-9Yostc5N4PxwiiSlhlwo,21124
31
+ pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
+ pyinfra/connectors/terraform.py,sha256=G7lK168Fz0jNFetc_7_bPT-RnoaRDksJat0R26fqkUk,3617
33
+ pyinfra/connectors/util.py,sha256=0bvoMsGMD-Tbfaer8NUhWJjBnaNKdmE83PDg48BYjcU,11374
34
+ pyinfra/connectors/vagrant.py,sha256=oEeRglzRmemRXW3vilsp_Xg9qnZMRprRJO9fd_C-f5M,4759
35
+ pyinfra/connectors/sshuserclient/__init__.py,sha256=Qc4RO2wknSWIiNTwOeQ0y2TeiuKHmyWDW2Dz4MOo9CE,44
36
+ pyinfra/connectors/sshuserclient/client.py,sha256=7YSd3QckZuPDRnKzy2FfG3J8zp7CY-jny8tbWwxvKro,9720
37
+ pyinfra/connectors/sshuserclient/config.py,sha256=UMwkvTgAIS7__re6Wz_pwH6EU4kO1-uMQ5zuFakH0v4,2721
38
+ pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
39
+ pyinfra/facts/apk.py,sha256=zihlHc6BbBUNQRL3v6wVFUHypfIL7wC6CPLrp4yynHo,518
40
+ pyinfra/facts/apt.py,sha256=8BJX79dBM9t3ceOhP_B5-I008HEJAUj_ijKDVvSxbFo,2035
41
+ pyinfra/facts/brew.py,sha256=aG77URXfNYT50ZpeHyyByiiX08GR6Oz6L0M3UMf9ztI,2302
42
+ pyinfra/facts/bsdinit.py,sha256=KyX4gc8cXKNn3O_NAlKWvEDKvMCnkTWJQg61oTV9ZN0,526
43
+ pyinfra/facts/cargo.py,sha256=mHtT2Yxoqx_g0akDq6jkHFN5VWNHQu70vKvNLZ10-b8,567
44
+ pyinfra/facts/choco.py,sha256=Tf7_DuQY0eZ4SIxV0rlyulrBCg7A0qyPbtTcHgkVP_0,739
45
+ pyinfra/facts/deb.py,sha256=dy5PKdQqy1oLdCUd6iTxCa6yG8agsaxZrPVsjvlDi74,1756
46
+ pyinfra/facts/dnf.py,sha256=7YNicP8EbRvWJQKU0OcwcW3bnIxx9GJ1Fy4qdCFnI0Y,898
47
+ pyinfra/facts/docker.py,sha256=Vr0rIEXSpK33yc2dDGct2lKhPQamvKEztxIAfwN0sk0,2046
48
+ pyinfra/facts/files.py,sha256=ki9-NQnYyt1VzuqKwEOYpX1nz5gJ_ph-ruq-4DS1VLg,11511
49
+ pyinfra/facts/gem.py,sha256=U1oF32LeiBo3ruaFm6hfF3XwNmgyhqamFUAeGn4cD3k,509
50
+ pyinfra/facts/git.py,sha256=rk4NS2SQJiosI6eY2eCy_p9kOP4O8UARRjFi16ObE2w,1294
51
+ pyinfra/facts/gpg.py,sha256=T5yVc_X9BLrDc77Yjk69x9SnP0qRWnKYEpi4JU__pKo,3765
52
+ pyinfra/facts/hardware.py,sha256=je1KjLfr4t-QZxTYei-V3Xqpll4TjbNeQxlHUvADg-c,11877
53
+ pyinfra/facts/iptables.py,sha256=sUkywfHZUXnMZF_KshAnyJufrJvZ9fBYnERSRbwOCRE,3374
54
+ pyinfra/facts/launchd.py,sha256=xjkszKmi9XNkovp7J33WTV19XfQbc5xGJkIBbFRTRhE,704
55
+ pyinfra/facts/lxd.py,sha256=ymBb_SdHbfAlx_tQyRPVa1ij_e68ZDODg1id1E0V8Rg,373
56
+ pyinfra/facts/mysql.py,sha256=0tpNks5be_mBWzIxuEqOyQ9MTYD9rRjeC3Oc_MQ2nns,6038
57
+ pyinfra/facts/npm.py,sha256=l_61BmbAWrO9RlYKUzLUcgicJ4RCMZHD70alrngC7-M,709
58
+ pyinfra/facts/openrc.py,sha256=NitG8BkwUCBrn-3SqM3FDiP4r6tAQTAWuHXTm1-3WGM,1386
59
+ pyinfra/facts/pacman.py,sha256=lU8IQYpQI3tzxRvKZHJD8giM_n17D5abzxIPasbII-A,1070
60
+ pyinfra/facts/pip.py,sha256=ORyrVxu_-8eIr0uSCkI6x4MH-sorDEavml4aozcdrOA,738
61
+ pyinfra/facts/pkg.py,sha256=2N-NlY_f3eQaW3dxCGKaKvu_ymC5_EF0-KASeul0EJc,488
62
+ pyinfra/facts/pkgin.py,sha256=Q1q7m4CCs_na92dDdQIjTuQnJ3axYv5qUdxnAy7e_XY,517
63
+ pyinfra/facts/postgres.py,sha256=M7ppmVvUXy8HSNPqrc8zLPyt8JamAyGs2IO2ocbzSZ8,4187
64
+ pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
65
+ pyinfra/facts/rpm.py,sha256=v9OCfTlTSqs4UQWDwnt2tVJkqHOmHZvj7Cd1g4mZ3WI,2091
66
+ pyinfra/facts/runit.py,sha256=uqwftOBmjWma09hNhIZ9ZF2QqqgdcA5NzrbBtP95lRM,1965
67
+ pyinfra/facts/selinux.py,sha256=N0zbJrAtBeRBtxZFUHbYTLQ2L4mRV7_Oj3Cj3OA1Npw,4272
68
+ pyinfra/facts/server.py,sha256=ocUPIIveHWfe4K4-7mt7Je7TswWbKmjdu1xAF2VxaD8,19798
69
+ pyinfra/facts/snap.py,sha256=MnZDllRZ1JKLw0SKRFQ1tI6Wi05gvipQPo7m4gpL4fI,1946
70
+ pyinfra/facts/systemd.py,sha256=7uYJyP9yryq9PZT0lRH597HHQFExxebz8og2OrRQUc8,3963
71
+ pyinfra/facts/sysvinit.py,sha256=PS7yMOrVxclywKjF3BIXmxTBgn2_vpSTQrDeOly_j8Q,1490
72
+ pyinfra/facts/upstart.py,sha256=4OSQ257KmYvWWboeXiqKGbhvOmLCMCwwOBgDXKCq1XE,579
73
+ pyinfra/facts/vzctl.py,sha256=LoWXJdFVg7bWcY-aGvSk-teFaAjUjKzQHnR-VuaomZ8,627
74
+ pyinfra/facts/xbps.py,sha256=k7G0L_IgOWIQUs-UswVgs3WzqdjokSCzq0TKT3Tvok0,517
75
+ pyinfra/facts/yum.py,sha256=5nhp7iXLFyEM7MSrBqierSQpTzqyBOwMB-e658Fe838,863
76
+ pyinfra/facts/zypper.py,sha256=hCgm37xwVqgrsG6h2FMNnzW17UH64y8qZbDOVrLglIY,802
77
+ pyinfra/facts/util/__init__.py,sha256=f7HKu8z9_yFC899ajJ3RFiyivioaZeGfOI6nf9GviCs,521
78
+ pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
79
+ pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGkrk,1181
80
+ pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
81
+ pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
82
+ pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
83
+ pyinfra/operations/apt.py,sha256=YAVZXzCE5zvPQb6FMTcQVVKhdKHUKkV8Ra6fRhD8rYA,13887
84
+ pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
85
+ pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
86
+ pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
87
+ pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
88
+ pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
89
+ pyinfra/operations/docker.py,sha256=Mra-m2iayXkc2LgCk2tuE6M7lZHhOGNJD3WQIPM9t2I,8396
90
+ pyinfra/operations/files.py,sha256=9O_HKgmVD_z74jtSivY4pKBPrCDKKHDSy0jAB9QERHU,53639
91
+ pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
92
+ pyinfra/operations/git.py,sha256=b26tQF_4hykTy0FtxiuCkqPk9i8JdZaz-RBhH4X96yw,11789
93
+ pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
94
+ pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
95
+ pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
96
+ pyinfra/operations/mysql.py,sha256=QcYvEQDlPESzDDoJ-HFwJFzN7ftsbsP892LMRZrmaLQ,19873
97
+ pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
98
+ pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
99
+ pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
100
+ pyinfra/operations/pip.py,sha256=7PpQvZHnwBGZ60V5b0XKNR4tHLW0MXJo6_6UX7HBtGY,5856
101
+ pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
102
+ pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
103
+ pyinfra/operations/postgres.py,sha256=LRoedDevQqiM5eX5Lmzb5mr_E9Od0ROVC0j18ZqaR0w,9661
104
+ pyinfra/operations/postgresql.py,sha256=agZjL2W4yxigk9ThIC0V_3wvmcWVdX308aJO24WkN6g,833
105
+ pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,861
106
+ pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
107
+ pyinfra/operations/runit.py,sha256=jRR5kt1OUCLbYktnu7yl3YvSiTW51VvEvOuB0yfd7Ww,5126
108
+ pyinfra/operations/selinux.py,sha256=khqWJsr9MOTyZmxP9P4dQ_7KUNlAfo5fx3Nv7kWm49w,5961
109
+ pyinfra/operations/server.py,sha256=wc5pNDQzEiwl9XEz1cG1m-51z1TV-5P39eMvSig90tY,36414
110
+ pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
111
+ pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
112
+ pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
113
+ pyinfra/operations/sysvinit.py,sha256=WzzthkmWL46MNNY6LsBZ90e37yPj0w2QyUtEAlGBwqY,4078
114
+ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE,1978
115
+ pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
116
+ pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
117
+ pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
118
+ pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
119
+ pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
120
+ pyinfra/operations/util/docker.py,sha256=6CvQgeFAXH_lDqKb7RxWpMvlCDwEAXlBaDZoJ8LxrYg,4596
121
+ pyinfra/operations/util/files.py,sha256=Zcet3ydNVbdT9jss0BDm6RJFyR_s6XTr0isDR60Zubw,3622
122
+ pyinfra/operations/util/packaging.py,sha256=xFtOlEX46ms7g3gDvOOInRVR1RVfgsmhLzFzsJAL_eU,9381
123
+ pyinfra/operations/util/service.py,sha256=kJd1zj4-sAaGIp5Ts7yAJznogWaGr8oQTztwenLAr7Y,1309
124
+ pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
125
+ pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
126
+ pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
127
+ pyinfra_cli/exceptions.py,sha256=iptx9Zj1od7VgSbOyXs7P8tD4zAZ_fwrQFKPlpPrfS0,4806
128
+ pyinfra_cli/inventory.py,sha256=lggXV9blVqNDT2lgVkq9FQWqYBPOakoLYLbuuVQ8upE,10179
129
+ pyinfra_cli/log.py,sha256=7WEGtmf3ncF1BtXL2icUjyxeRKy-7XrCcQ2Hg4GWX5Y,2201
130
+ pyinfra_cli/main.py,sha256=MsBn0RCD5ce4GY-dDBe6vLXT0LanDrrQZpJOTIYZPBs,19715
131
+ pyinfra_cli/prints.py,sha256=za6V-yjXf-LViBW73qWcyfsajCUnf0NCG-7K0ugOA0k,9170
132
+ pyinfra_cli/util.py,sha256=f3iGIPxlUiQJ5LmUGYbEz0QrySQAKmf9xov9WvHXbrk,6364
133
+ pyinfra_cli/virtualenv.py,sha256=6j9W54JkQLN02SrZZIVwszp0GxlaaDEUWFZjBDHIWNA,2466
134
+ tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
+ tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
136
+ tests/test_api/test_api_arguments.py,sha256=5k7w0_x5cnABEFOk0LQBCt5gU9iTN9lo2XS6MlJTnhQ,1961
137
+ tests/test_api/test_api_command.py,sha256=OW0ESMyS5vo38u17DHeCrSIaIkW9gMU5PSkXL7mRrq0,3204
138
+ tests/test_api/test_api_config.py,sha256=bf0mDrUie3On6zGC_hJBpv-wvSf3LHBIBzUDvkopEt0,708
139
+ tests/test_api/test_api_deploys.py,sha256=h_zbI6CK4K8SdzEr3LEAMPxOf9hnQBdi_suqiNPqHHQ,4200
140
+ tests/test_api/test_api_facts.py,sha256=fUPadZbZ5xaKSF-kmLj7XGwsNiBmfj7Av0gl8fE01Qc,10687
141
+ tests/test_api/test_api_host.py,sha256=U_VW2vTl35vR8EdyIGMKr4y0ydsDLbvHSjZDa99CyNE,1119
142
+ tests/test_api/test_api_inventory.py,sha256=VLbV0MXdRLOPvTXJF156ne6rAx1cBlFfgq_1S79s4tw,2013
143
+ tests/test_api/test_api_operations.py,sha256=GUfnuHK2NoTAGdOT4AbytT9R8i3ZZIvGP7KBfoYcYUQ,20134
144
+ tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
145
+ tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
+ tests/test_cli/test_cli.py,sha256=nZQH0zK1SOgIr5VxBuuHH68hyzG4lhwyw_Z7EhjPtf4,6045
147
+ tests/test_cli/test_cli_deploy.py,sha256=KBnnDsiD21h7t1S2JXpEDpiMxh0AFqwxaEl0z78IE9E,4858
148
+ tests/test_cli/test_cli_exceptions.py,sha256=02sjC6rMptuqchgcdjdsVNQbSQYW6HwGutSy6Q6sMs4,3088
149
+ tests/test_cli/test_cli_util.py,sha256=-Ehnj0cO-EkF-6KLxcPPcFeuAUMTz-fKITrxhuiYhV4,2562
150
+ tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
151
+ tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
152
+ tests/test_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
153
+ tests/test_connectors/test_chroot.py,sha256=QK7YgFPXzHh8y363-tmHvzZ0Ok5PVJWFTDAvwt91eac,5907
154
+ tests/test_connectors/test_docker.py,sha256=0EjkfhCHpLCfL4X-AIdMNw5ASaseY0tbRAn7T_TAkMQ,6566
155
+ tests/test_connectors/test_dockerssh.py,sha256=MaC9IK1OZDiqoIsuLOZBJnPDglsMoPDoL19LQtXsyCE,9303
156
+ tests/test_connectors/test_local.py,sha256=N_FkejDZKu7XLnKeApqfBARYMyxf-hRXCQJrXLHvwRg,7442
157
+ tests/test_connectors/test_ssh.py,sha256=zYL0FbRXzqkYJslhmVeUgSkcHtozhmvZfRcaqDrYKvI,40386
158
+ tests/test_connectors/test_sshuserclient.py,sha256=2PQNLPhNL6lBACc6tQuXmPoog-9L6AdDQNrA-rEw1_8,5734
159
+ tests/test_connectors/test_terraform.py,sha256=Z5MhgDeRDFumu-GlbjMD0ZRkecwBIPP8C8ZVg-mq7C8,3743
160
+ tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
161
+ tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
162
+ pyinfra-3.0.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
163
+ pyinfra-3.0.dist-info/METADATA,sha256=2hewmitxy4sio520Cdl_pYYey1Df-zj80v_CjH6dZsI,8320
164
+ pyinfra-3.0.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
165
+ pyinfra-3.0.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
166
+ pyinfra-3.0.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
167
+ pyinfra-3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.3)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -0,0 +1,11 @@
1
+ [console_scripts]
2
+ pyinfra = pyinfra_cli.__main__:execute_pyinfra
3
+
4
+ [pyinfra.connectors]
5
+ chroot = pyinfra.connectors.chroot:ChrootConnector
6
+ docker = pyinfra.connectors.docker:DockerConnector
7
+ dockerssh = pyinfra.connectors.dockerssh:DockerSSHConnector
8
+ local = pyinfra.connectors.local:LocalConnector
9
+ ssh = pyinfra.connectors.ssh:SSHConnector
10
+ terraform = pyinfra.connectors.terraform:TerraformInventoryConnector
11
+ vagrant = pyinfra.connectors.vagrant:VagrantInventoryConnector
pyinfra_cli/__main__.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
1
  import signal
3
2
  import sys
4
3
 
@@ -15,12 +14,14 @@ pyinfra.is_cli = True
15
14
  # Don't write out deploy.pyc/config.pyc etc
16
15
  sys.dont_write_bytecode = True
17
16
 
17
+ sys.path.append(".")
18
+
18
19
  # Shut it click
19
20
  click.disable_unicode_literals_warning = True # type: ignore
20
21
 
21
22
  # Force line buffering
22
- sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 1)
23
- sys.stderr = os.fdopen(sys.stderr.fileno(), "w", 1)
23
+ sys.stdout.reconfigure(line_buffering=True) # type: ignore
24
+ sys.stderr.reconfigure(line_buffering=True) # type: ignore
24
25
 
25
26
 
26
27
  def _handle_interrupt(signum, frame):
pyinfra_cli/commands.py CHANGED
@@ -1,5 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import json
2
4
 
5
+ from pyinfra.api.facts import FactBase
6
+
3
7
  from .exceptions import CliError
4
8
  from .util import parse_cli_arg, try_import_module_attribute
5
9
 
@@ -32,7 +36,7 @@ def get_func_and_args(commands):
32
36
 
33
37
 
34
38
  def get_facts_and_args(commands):
35
- facts = []
39
+ facts: list[tuple[FactBase, tuple, dict]] = []
36
40
 
37
41
  current_fact = None
38
42
 
@@ -42,7 +46,7 @@ def get_facts_and_args(commands):
42
46
  raise CliError("Invalid fact commands: `{0}`".format(commands))
43
47
 
44
48
  key, value = command.split("=", 1)
45
- current_fact[2][key] = value
49
+ current_fact[2][key] = parse_cli_arg(value)
46
50
  continue
47
51
 
48
52
  if current_fact:
@@ -53,6 +57,7 @@ def get_facts_and_args(commands):
53
57
  raise CliError(f"Invalid fact: `{command}`, should be in the format `module.cls`")
54
58
 
55
59
  fact_cls = try_import_module_attribute(command, prefix="pyinfra.facts")
60
+ assert fact_cls is not None
56
61
  current_fact = (fact_cls, (), {})
57
62
 
58
63
  if current_fact:
pyinfra_cli/exceptions.py CHANGED
@@ -1,67 +1,103 @@
1
+ import abc
1
2
  import sys
2
- from traceback import format_exception, format_tb
3
+ from inspect import getframeinfo
4
+ from traceback import format_exception, format_tb, walk_tb
5
+ from types import TracebackType
3
6
 
4
7
  import click
5
8
 
6
- from pyinfra import host, logger, state
7
- from pyinfra.api.exceptions import PyinfraError
8
- from pyinfra.context import ctx_host
9
-
10
-
11
- class CliError(PyinfraError, click.ClickException):
12
- def show(self):
9
+ from pyinfra import logger
10
+ from pyinfra.api.exceptions import (
11
+ ArgumentTypeError,
12
+ ConnectorDataTypeError,
13
+ OperationError,
14
+ PyinfraError,
15
+ )
16
+ from pyinfra.api.util import PYINFRA_INSTALL_DIR
17
+
18
+
19
+ def get_frame_line_from_tb(tb: TracebackType):
20
+ frame_lines = list(walk_tb(tb))
21
+ frame_lines.reverse()
22
+ for frame, line in frame_lines:
23
+ info = getframeinfo(frame)
24
+ if info.filename.startswith(PYINFRA_INSTALL_DIR):
25
+ continue
26
+ return info
27
+
28
+
29
+ class WrappedError(click.ClickException):
30
+ def __init__(self, e: Exception):
31
+ self.traceback = e.__traceback__
32
+ self.exception = e
33
+
34
+ # Pull message from the wrapped exception
35
+ message = getattr(e, "message", e.args[0])
36
+ if not isinstance(message, str):
37
+ message = repr(message)
38
+ self.message = message
39
+
40
+ def show(self, file=None):
13
41
  name = "unknown error"
14
42
 
15
- if isinstance(self, PyinfraError):
43
+ if isinstance(self.exception, ConnectorDataTypeError):
44
+ name = "Connector data type error"
45
+ elif isinstance(self.exception, ArgumentTypeError):
46
+ name = "Argument type error"
47
+ elif isinstance(self.exception, OperationError):
48
+ name = "Operation error"
49
+ elif isinstance(self.exception, PyinfraError):
16
50
  name = "pyinfra error"
51
+ elif isinstance(self.exception, IOError):
52
+ name = "Local IO error"
53
+
54
+ if self.traceback:
55
+ info = get_frame_line_from_tb(self.traceback)
56
+ if info:
57
+ name = f"{name} in {info.filename} line {info.lineno}"
58
+
59
+ logger.warning(
60
+ "--> {0}: {1}".format(
61
+ click.style(name, "red", bold=True),
62
+ self,
63
+ ),
64
+ )
17
65
 
18
- elif isinstance(self, IOError):
19
- name = "local IO error"
20
-
21
- if ctx_host.isset():
22
- # Get any operation meta + name
23
- op_name = None
24
- current_op_hash = host.current_op_hash
25
- current_op_meta = state.op_meta.get(current_op_hash)
26
- if current_op_meta:
27
- op_name = ", ".join(current_op_meta["names"])
28
-
29
- sys.stderr.write(
30
- "--> {0}{1}{2}: ".format(
31
- host.print_prefix,
32
- click.style(name, "red", bold=True),
33
- " (operation={0})".format(op_name) if op_name else "",
34
- ),
35
- )
36
- else:
37
- sys.stderr.write(
38
- "--> {0}: ".format(click.style(name, "red", bold=True)),
39
- )
40
66
 
41
- logger.warning(self)
67
+ class CliError(click.ClickException):
68
+ def show(self, file=None):
69
+ logger.warning(
70
+ "--> {0}: {1}".format(
71
+ click.style("pyinfra error", "red", bold=True),
72
+ self,
73
+ ),
74
+ )
75
+
42
76
 
77
+ class UnexpectedMixin(abc.ABC):
78
+ exception: Exception
79
+ traceback: TracebackType
43
80
 
44
- class UnexpectedMixin:
45
81
  def get_traceback_lines(self):
46
- traceback = getattr(self.e, "_traceback")
82
+ traceback = getattr(self.exception, "_traceback")
47
83
  return format_tb(traceback)
48
84
 
49
85
  def get_traceback(self):
50
86
  return "".join(self.get_traceback_lines())
51
87
 
52
88
  def get_exception(self):
53
- return "".join(format_exception(self.e.__class__, self.e, None))
89
+ return "".join(format_exception(self.exception.__class__, self.exception, None))
54
90
 
55
91
 
56
92
  class UnexpectedExternalError(click.ClickException, UnexpectedMixin):
57
93
  def __init__(self, e, filename):
58
94
  _, _, traceback = sys.exc_info()
59
95
  e._traceback = traceback
60
- self.e = e
96
+ self.exception = e
61
97
  self.filename = filename
62
98
 
63
- def show(self):
64
- click.echo(
99
+ def show(self, file=None):
100
+ logger.warning(
65
101
  "--> {0}:\n".format(
66
102
  click.style(
67
103
  "An exception occurred in: {0}".format(self.filename),
@@ -69,9 +105,9 @@ class UnexpectedExternalError(click.ClickException, UnexpectedMixin):
69
105
  bold=True,
70
106
  ),
71
107
  ),
72
- err=True,
73
108
  )
74
109
 
110
+ click.echo("Traceback (most recent call last):", err=True)
75
111
  click.echo(self.get_traceback(), err=True, nl=False)
76
112
  click.echo(self.get_exception(), err=True)
77
113
 
@@ -80,9 +116,9 @@ class UnexpectedInternalError(click.ClickException, UnexpectedMixin):
80
116
  def __init__(self, e):
81
117
  _, _, traceback = sys.exc_info()
82
118
  e._traceback = traceback
83
- self.e = e
119
+ self.exception = e
84
120
 
85
- def show(self):
121
+ def show(self, file=None):
86
122
  click.echo(
87
123
  "--> {0}:\n".format(
88
124
  click.style(
@@ -99,7 +135,7 @@ class UnexpectedInternalError(click.ClickException, UnexpectedMixin):
99
135
 
100
136
  # Syntax errors contain the filename/line/etc, but other exceptions
101
137
  # don't, so print the *last* call to stderr.
102
- if not isinstance(self.e, SyntaxError):
138
+ if not isinstance(self.exception, SyntaxError):
103
139
  sys.stderr.write(traceback_lines[-1])
104
140
 
105
141
  exception = self.get_exception()
pyinfra_cli/inventory.py CHANGED
@@ -1,3 +1,4 @@
1
+ import socket
1
2
  from collections import defaultdict
2
3
  from os import listdir, path
3
4
  from types import GeneratorType
@@ -79,19 +80,52 @@ def _get_any_tuple_first(item: Union[T, Tuple[T, Any]]) -> T:
79
80
  return item[0] if isinstance(item, tuple) else item
80
81
 
81
82
 
83
+ def _resolves_to_host(maybe_host: str) -> bool:
84
+ """Check if a string resolves to a valid IP address."""
85
+ try:
86
+ # Use getaddrinfo to support IPv6 hosts
87
+ socket.getaddrinfo(maybe_host, port=None)
88
+ return True
89
+ except socket.gaierror:
90
+ return False
91
+
92
+
82
93
  def make_inventory(
83
94
  inventory: str,
84
95
  override_data=None,
85
96
  cwd: Optional[str] = None,
86
97
  group_data_directories=None,
87
98
  ):
88
- # First, try loading the inventory as if it's a Python import function
89
- try:
90
- inventory_func = try_import_module_attribute(inventory)
91
- except (CliError, ValueError):
92
- # If not an import, load as if from the filesystem *or* comma separated list, which also
93
- # loads any all.py group data files (imported functions do not load group data).
99
+ # (Un)fortunately the CLI is pretty flexible for inventory inputs; we support inventory files, a
100
+ # single hostname, list of hosts, connectors, and python module.function or module:function
101
+ # imports.
102
+ #
103
+ # We check first for an inventory file, a list of hosts or anything with a connector, because
104
+ # (1) an inventory file is a common use case and (2) no other option can have a comma or an @
105
+ # symbol in them.
106
+ is_path_or_host_list_or_connector = (
107
+ path.exists(inventory) or "," in inventory or "@" in inventory
108
+ )
109
+ if not is_path_or_host_list_or_connector:
110
+ # Next, try loading the inventory from a python function. This happens before checking for a
111
+ # single-host inventory, so that your command does not stop working because somebody
112
+ # registered the domain `my.module.name`.
113
+ inventory_func = try_import_module_attribute(inventory, raise_for_none=False)
114
+
115
+ # If the inventory does not refer to a module, we finally check if it refers to a reachable
116
+ # host
117
+ if inventory_func is None and _resolves_to_host(inventory):
118
+ is_path_or_host_list_or_connector = True
119
+
120
+ if is_path_or_host_list_or_connector:
121
+ # The inventory is either an inventory file or a (list of) hosts
94
122
  return make_inventory_from_files(inventory, override_data, cwd, group_data_directories)
123
+ elif inventory_func is None:
124
+ logger.warn(
125
+ f"{inventory} is neither an inventory file, a (list of) hosts or connectors "
126
+ "nor refers to a python module"
127
+ )
128
+ return Inventory.empty()
95
129
  else:
96
130
  return make_inventory_from_func(inventory_func, override_data)
97
131