pyinfra 3.1.1__tar.gz → 3.3__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 (201) hide show
  1. {pyinfra-3.1.1 → pyinfra-3.3}/CHANGELOG.md +92 -0
  2. {pyinfra-3.1.1 → pyinfra-3.3}/LICENSE.md +1 -1
  3. {pyinfra-3.1.1/pyinfra.egg-info → pyinfra-3.3}/PKG-INFO +25 -25
  4. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/arguments.py +9 -2
  5. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/arguments_typed.py +4 -5
  6. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/command.py +22 -3
  7. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/config.py +5 -2
  8. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/deploy.py +4 -2
  9. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/facts.py +3 -0
  10. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/host.py +15 -7
  11. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/operation.py +2 -1
  12. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/state.py +1 -1
  13. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/base.py +34 -8
  14. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/chroot.py +7 -2
  15. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/docker.py +24 -8
  16. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/dockerssh.py +7 -2
  17. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/local.py +7 -2
  18. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/ssh.py +9 -2
  19. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/sshuserclient/client.py +42 -14
  20. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/sshuserclient/config.py +2 -0
  21. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/terraform.py +1 -1
  22. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/util.py +13 -9
  23. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/context.py +9 -2
  24. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/apk.py +8 -1
  25. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/apt.py +68 -0
  26. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/brew.py +13 -0
  27. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/bsdinit.py +3 -0
  28. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/cargo.py +5 -0
  29. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/choco.py +6 -0
  30. pyinfra-3.3/pyinfra/facts/crontab.py +195 -0
  31. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/deb.py +10 -0
  32. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/dnf.py +5 -0
  33. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/docker.py +16 -0
  34. pyinfra-3.3/pyinfra/facts/efibootmgr.py +113 -0
  35. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/files.py +112 -7
  36. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/flatpak.py +7 -0
  37. pyinfra-3.3/pyinfra/facts/freebsd.py +75 -0
  38. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/gem.py +5 -0
  39. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/git.py +12 -2
  40. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/gpg.py +7 -0
  41. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/hardware.py +13 -0
  42. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/iptables.py +9 -1
  43. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/launchd.py +5 -0
  44. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/lxd.py +5 -0
  45. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/mysql.py +9 -2
  46. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/npm.py +5 -0
  47. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/openrc.py +8 -0
  48. pyinfra-3.3/pyinfra/facts/opkg.py +245 -0
  49. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/pacman.py +9 -1
  50. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/pip.py +5 -0
  51. pyinfra-3.3/pyinfra/facts/pipx.py +82 -0
  52. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/pkg.py +4 -0
  53. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/pkgin.py +5 -0
  54. pyinfra-3.3/pyinfra/facts/podman.py +54 -0
  55. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/postgres.py +10 -2
  56. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/rpm.py +11 -0
  57. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/runit.py +7 -0
  58. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/selinux.py +16 -0
  59. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/server.py +87 -79
  60. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/snap.py +7 -0
  61. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/systemd.py +5 -0
  62. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/sysvinit.py +4 -0
  63. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/upstart.py +5 -0
  64. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/util/__init__.py +4 -1
  65. pyinfra-3.3/pyinfra/facts/util/units.py +30 -0
  66. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/vzctl.py +5 -0
  67. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/xbps.py +6 -1
  68. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/yum.py +5 -0
  69. pyinfra-3.3/pyinfra/facts/zfs.py +77 -0
  70. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/zypper.py +5 -0
  71. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/local.py +3 -2
  72. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/apt.py +36 -22
  73. pyinfra-3.3/pyinfra/operations/crontab.py +189 -0
  74. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/docker.py +61 -56
  75. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/files.py +65 -1
  76. pyinfra-3.3/pyinfra/operations/freebsd/__init__.py +12 -0
  77. pyinfra-3.3/pyinfra/operations/freebsd/freebsd_update.py +70 -0
  78. pyinfra-3.3/pyinfra/operations/freebsd/pkg.py +219 -0
  79. pyinfra-3.3/pyinfra/operations/freebsd/service.py +116 -0
  80. pyinfra-3.3/pyinfra/operations/freebsd/sysrc.py +92 -0
  81. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/git.py +23 -7
  82. pyinfra-3.3/pyinfra/operations/opkg.py +88 -0
  83. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/pip.py +3 -2
  84. pyinfra-3.3/pyinfra/operations/pipx.py +90 -0
  85. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/postgres.py +114 -27
  86. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/runit.py +2 -0
  87. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/server.py +9 -181
  88. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/util/docker.py +44 -22
  89. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/zfs.py +3 -3
  90. {pyinfra-3.1.1 → pyinfra-3.3/pyinfra.egg-info}/PKG-INFO +25 -25
  91. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra.egg-info/SOURCES.txt +16 -0
  92. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra.egg-info/requires.txt +23 -22
  93. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/exceptions.py +5 -0
  94. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/inventory.py +26 -9
  95. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/log.py +3 -0
  96. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/main.py +9 -8
  97. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/prints.py +19 -4
  98. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/util.py +3 -0
  99. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/virtualenv.py +1 -1
  100. {pyinfra-3.1.1 → pyinfra-3.3}/pyproject.toml +1 -0
  101. {pyinfra-3.1.1 → pyinfra-3.3}/setup.py +13 -14
  102. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_cli_deploy.py +15 -13
  103. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_cli_inventory.py +53 -0
  104. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_ssh.py +302 -182
  105. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_sshuserclient.py +68 -1
  106. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_facts.py +13 -6
  107. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_operations.py +6 -6
  108. pyinfra-3.3/tests/test_units.py +30 -0
  109. pyinfra-3.1.1/pyinfra/facts/zfs.py +0 -57
  110. {pyinfra-3.1.1 → pyinfra-3.3}/MANIFEST.in +0 -0
  111. {pyinfra-3.1.1 → pyinfra-3.3}/README.md +0 -0
  112. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/__init__.py +0 -0
  113. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/__main__.py +0 -0
  114. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/__init__.py +0 -0
  115. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/connect.py +0 -0
  116. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/connectors.py +0 -0
  117. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/exceptions.py +0 -0
  118. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/inventory.py +0 -0
  119. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/operations.py +0 -0
  120. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/api/util.py +0 -0
  121. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/__init__.py +0 -0
  122. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/ssh_util.py +0 -0
  123. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/sshuserclient/__init__.py +0 -0
  124. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/connectors/vagrant.py +0 -0
  125. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/__init__.py +0 -0
  126. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/postgresql.py +0 -0
  127. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/util/databases.py +0 -0
  128. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/util/packaging.py +0 -0
  129. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/facts/util/win_files.py +0 -0
  130. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/__init__.py +0 -0
  131. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/apk.py +0 -0
  132. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/brew.py +0 -0
  133. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/bsdinit.py +0 -0
  134. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/cargo.py +0 -0
  135. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/choco.py +0 -0
  136. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/dnf.py +0 -0
  137. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/flatpak.py +0 -0
  138. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/gem.py +0 -0
  139. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/iptables.py +0 -0
  140. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/launchd.py +0 -0
  141. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/lxd.py +0 -0
  142. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/mysql.py +0 -0
  143. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/npm.py +0 -0
  144. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/openrc.py +0 -0
  145. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/pacman.py +0 -0
  146. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/pkg.py +0 -0
  147. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/pkgin.py +0 -0
  148. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/postgresql.py +0 -0
  149. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/puppet.py +0 -0
  150. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/python.py +0 -0
  151. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/selinux.py +0 -0
  152. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/snap.py +0 -0
  153. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/ssh.py +0 -0
  154. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/systemd.py +0 -0
  155. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/sysvinit.py +0 -0
  156. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/upstart.py +0 -0
  157. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/util/__init__.py +0 -0
  158. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/util/files.py +0 -0
  159. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/util/packaging.py +0 -0
  160. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/util/service.py +0 -0
  161. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/vzctl.py +0 -0
  162. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/xbps.py +0 -0
  163. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/yum.py +0 -0
  164. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/operations/zypper.py +0 -0
  165. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/progress.py +0 -0
  166. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/py.typed +0 -0
  167. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra/version.py +0 -0
  168. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra.egg-info/dependency_links.txt +0 -0
  169. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra.egg-info/entry_points.txt +0 -0
  170. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra.egg-info/top_level.txt +0 -0
  171. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/__init__.py +0 -0
  172. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/__main__.py +0 -0
  173. {pyinfra-3.1.1 → pyinfra-3.3}/pyinfra_cli/commands.py +0 -0
  174. {pyinfra-3.1.1 → pyinfra-3.3}/setup.cfg +0 -0
  175. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/__init__.py +0 -0
  176. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api.py +0 -0
  177. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_arguments.py +0 -0
  178. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_command.py +0 -0
  179. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_config.py +0 -0
  180. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_deploys.py +0 -0
  181. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_facts.py +0 -0
  182. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_host.py +0 -0
  183. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_inventory.py +0 -0
  184. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_operations.py +0 -0
  185. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_api/test_api_util.py +0 -0
  186. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/__init__.py +0 -0
  187. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_cli.py +0 -0
  188. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_cli_exceptions.py +0 -0
  189. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_cli_util.py +0 -0
  190. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/test_context_objects.py +0 -0
  191. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_cli/util.py +0 -0
  192. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/__init__.py +0 -0
  193. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_chroot.py +0 -0
  194. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_docker.py +0 -0
  195. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_dockerssh.py +0 -0
  196. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_local.py +0 -0
  197. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_terraform.py +0 -0
  198. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_util.py +0 -0
  199. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_connectors/test_vagrant.py +0 -0
  200. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_global_arguments.py +0 -0
  201. {pyinfra-3.1.1 → pyinfra-3.3}/tests/test_operations_utils.py +0 -0
@@ -1,3 +1,95 @@
1
+ # v3.3
2
+
3
+ Second release of 2025: loads of adds, fixes and documentation improvements. A huge THANK YOU to all contributors. Slightly changed format for the change list based on commit messages which should speed up releases:
4
+
5
+ New operations & arguments:
6
+
7
+ - operations/freebsd: add FreeBSD operations & facts (@DtxdF)
8
+ - operations/files.move: new operation (@Pirols)
9
+ - operations/server.user: enable adding user to secondary groups (Pirols)
10
+ - operations/postgres: enhance role management by adding `ALTER ROLE` support (@wowi42)
11
+ - operations/postgres: enable modifying existing postgres databases (@wowi42)
12
+ - operations/docker.container: refactor to support container recreation (@minor-fixes)
13
+
14
+ Operation/fact fixes:
15
+
16
+ - operations/postgres: fix quoting of locale parameters (@xvello)
17
+ - operations/server: remove leftover deprecated parameter (@wowi42)
18
+ - operations/pacmen: update PACMAN_REGEX to support additional characters (@wowi42)
19
+ - operations/server.sysctl: handle 0 integer values correctly (@lemmi)
20
+ - operations/apt: dist-upgrade also supports --autoremove (@bauen1)
21
+ - operations/apt: fix parameter name in docs (@bauen1)
22
+ - operations/server: fix: lastlog is always null (@ingstem)
23
+ - operations/docker: Fixed a typo with the volumes parameter to docker.prune operation (@mpilone)
24
+ - facts/xbps.XbpsPackages: allow . in package names (@lemmi)
25
+
26
+ Connectors, CLI:
27
+
28
+ - connectors: improve detection of sudo password needed
29
+ - connectors/ssh: add support for `ServerAliveInterval` (@chipot)
30
+ - cli: enable -h as shorthand for --help (@NichtJens)
31
+
32
+ Docs:
33
+
34
+ - docs: Add a section explaining connector flow (@goetzk)
35
+ - docs: Add inventory processing note and reference it (@goetzk)
36
+ - docs: Add example of logging to using operations docs (@goetzk)
37
+ - docs: fix wrong example operation using forbidden argument 'name' (@robertmx)
38
+ - docs: Add a note to the docs about using `_inner` when calling operations from other operations (@CSDUMMI)
39
+ - docs: Document host, state, inventory in files.template (@mpilone)
40
+ - docs: Minor adjustments to wording help docs and help (@goetzk)
41
+ - docs: expand connectors documentation (@goetzk)
42
+ - docs: correct import path for any_changed, all_changed (@lemmi)
43
+ - docs: Add note re: global arguments to operations (@simonhammes)
44
+
45
+ Internal/meta:
46
+
47
+ - refactor: update opkg documentation and add requires_command to ZFS and Git tests (@wowi42)
48
+ - Update testing and development dependencies in setup.py (@wowi42)
49
+ - tests: Load test specs with PyYAML instead of json (@taliaferro)
50
+ - typing: Require explicit override decorator (@bauen1)
51
+ - api: don't execute callbacks within a greenlet if we're already in one
52
+ - ci: Github Actions support for python 3.12 (@wowi42)
53
+ - ci: Prevent docs job from running on forks (@simonhammes)
54
+
55
+ # v3.2
56
+
57
+ Hello 2025! Here's pyinfra 3.2 - with another incredible round of contributions from the community, THANK YOU ALL. New stuff:
58
+
59
+ - Add total counts to results summary (@NichtJens)
60
+ - Enable passing extra data via `local.include` (@TimothyWillard)
61
+ - Validate inventory files and display warnings for unexpected variables (@simonhammes)
62
+
63
+ New operations/facts:
64
+
65
+ - Add `pipx` operations (`packages`, `upgrade_all`, `ensure_path`) facts (`PipxPackages`, `PipxEnvironment`) and operations (@maisim)
66
+ - Add `server.OsRelease` fact (@wowi42)
67
+ - Add `podman.PodmanSystemInfo` and `podman.PodmanPs` facts (@bauen1)
68
+ - Add many extra arguments (including generic args) to `files.FindFiles*` facts (@JakkuSakura)
69
+ - Add `system` argument to `git.config` operation (@Pirols)
70
+ - Add `psql_database` argument to postgres operations & facts (@hamishfagg)
71
+ - Add `files.Sha384File` fact and `sha384sum` argument to `files.download` operation (@simonhammes)
72
+ - Add `apt.SimulateOperationWillChange` fact (@bauen1)
73
+ - Detect changes in `apt.upgrade` and `apt.dist_upgrade` operations (@bauen1)
74
+ - Add `fibootmgr.EFIBootMgr` fact (@bauen1)
75
+ - Add opkg facts and operations (@morrison12)
76
+
77
+ Fixes:
78
+
79
+ - Multiple fixes for `server.crontab` operation and facts (@JakkuSakura)
80
+ - Correctly handle `latest` argument with requirements file in `pip.packages` operation (@amiraliakbari)
81
+ - Fix regex used to parse installed apk packages (@simonhammes)
82
+ - Fix SSH connector overwriting known hosts files (@vo452)
83
+
84
+ Docs/internal tweaks:
85
+
86
+ - Add type annotations for many more operations (@simonhammes)
87
+ - Add typos CI checking to replace flake8-spellcheck (@simonhammes)
88
+ - Bump CI actions and dependencies (@simonhammes)
89
+ - Require JSON tests to include all arguments
90
+ - Remove unused `configparser` dependency (@bkmgit)
91
+ - Many small documentation fixes/tweaks
92
+
1
93
  # v3.1.1
2
94
 
3
95
  - Improve errors with 2.x style `@decorator` (vs `@decorator()`) functions
@@ -1,4 +1,4 @@
1
- Copyright (C) 2020 Nick Barrett <nick@fizzadar.com>
1
+ Copyright (C) 2025 Nick Barrett <nick@fizzadar.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1.1
3
+ Version: 3.3
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -16,7 +16,6 @@ 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.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
@@ -24,7 +23,7 @@ Classifier: Programming Language :: Python :: 3.12
24
23
  Classifier: Topic :: System :: Systems Administration
25
24
  Classifier: Topic :: System :: Installation/Setup
26
25
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
26
+ Requires-Python: >=3.9
28
27
  Description-Content-Type: text/markdown
29
28
  License-File: LICENSE.md
30
29
  Requires-Dist: gevent>=1.5
@@ -33,7 +32,6 @@ Requires-Dist: click>2
33
32
  Requires-Dist: jinja2<4,>2
34
33
  Requires-Dist: python-dateutil<3,>2
35
34
  Requires-Dist: setuptools
36
- Requires-Dist: configparser
37
35
  Requires-Dist: pywinrm
38
36
  Requires-Dist: typeguard
39
37
  Requires-Dist: distro<2,>=1.6
@@ -42,14 +40,15 @@ Requires-Dist: graphlib_backport; python_version < "3.9"
42
40
  Requires-Dist: typing-extensions; python_version < "3.11"
43
41
  Requires-Dist: importlib_metadata>=3.6; python_version < "3.10"
44
42
  Provides-Extra: test
45
- Requires-Dist: pytest==8.2.1; extra == "test"
46
- Requires-Dist: coverage==7.5.1; extra == "test"
47
- Requires-Dist: pytest-cov==5.0.0; extra == "test"
48
- Requires-Dist: black==24.4.2; extra == "test"
49
- Requires-Dist: isort==5.13.2; extra == "test"
50
- Requires-Dist: flake8==7.0.0; extra == "test"
43
+ Requires-Dist: pytest==8.3.5; extra == "test"
44
+ Requires-Dist: coverage==7.7.1; extra == "test"
45
+ Requires-Dist: pytest-cov==6.0.0; extra == "test"
46
+ Requires-Dist: black==25.1.0; extra == "test"
47
+ Requires-Dist: isort==6.0.1; extra == "test"
48
+ Requires-Dist: flake8==7.1.2; extra == "test"
51
49
  Requires-Dist: flake8-black==0.3.6; extra == "test"
52
- Requires-Dist: flake8-isort==6.1.1; extra == "test"
50
+ Requires-Dist: flake8-isort==6.1.2; extra == "test"
51
+ Requires-Dist: pyyaml==6.0.2; extra == "test"
53
52
  Requires-Dist: mypy; extra == "test"
54
53
  Requires-Dist: types-cryptography; extra == "test"
55
54
  Requires-Dist: types-paramiko; extra == "test"
@@ -57,33 +56,34 @@ Requires-Dist: types-python-dateutil; extra == "test"
57
56
  Requires-Dist: types-PyYAML; extra == "test"
58
57
  Requires-Dist: types-setuptools; extra == "test"
59
58
  Provides-Extra: docs
60
- Requires-Dist: pyinfra-guzzle_sphinx_theme==0.16; extra == "docs"
61
- Requires-Dist: myst-parser==2.0.0; extra == "docs"
62
- Requires-Dist: sphinx==6.2.1; extra == "docs"
59
+ Requires-Dist: pyinfra-guzzle_sphinx_theme==0.17; extra == "docs"
60
+ Requires-Dist: myst-parser==4.0.1; extra == "docs"
61
+ Requires-Dist: sphinx==8.2.3; extra == "docs"
63
62
  Provides-Extra: dev
64
- Requires-Dist: pytest==8.2.1; extra == "dev"
65
- Requires-Dist: coverage==7.5.1; extra == "dev"
66
- Requires-Dist: pytest-cov==5.0.0; extra == "dev"
67
- Requires-Dist: black==24.4.2; extra == "dev"
68
- Requires-Dist: isort==5.13.2; extra == "dev"
69
- Requires-Dist: flake8==7.0.0; extra == "dev"
63
+ Requires-Dist: pytest==8.3.5; extra == "dev"
64
+ Requires-Dist: coverage==7.7.1; extra == "dev"
65
+ Requires-Dist: pytest-cov==6.0.0; extra == "dev"
66
+ Requires-Dist: black==25.1.0; extra == "dev"
67
+ Requires-Dist: isort==6.0.1; extra == "dev"
68
+ Requires-Dist: flake8==7.1.2; extra == "dev"
70
69
  Requires-Dist: flake8-black==0.3.6; extra == "dev"
71
- Requires-Dist: flake8-isort==6.1.1; extra == "dev"
70
+ Requires-Dist: flake8-isort==6.1.2; extra == "dev"
71
+ Requires-Dist: pyyaml==6.0.2; extra == "dev"
72
72
  Requires-Dist: mypy; extra == "dev"
73
73
  Requires-Dist: types-cryptography; extra == "dev"
74
74
  Requires-Dist: types-paramiko; extra == "dev"
75
75
  Requires-Dist: types-python-dateutil; extra == "dev"
76
76
  Requires-Dist: types-PyYAML; extra == "dev"
77
77
  Requires-Dist: types-setuptools; extra == "dev"
78
- Requires-Dist: pyinfra-guzzle_sphinx_theme==0.16; extra == "dev"
79
- Requires-Dist: myst-parser==2.0.0; extra == "dev"
80
- Requires-Dist: sphinx==6.2.1; extra == "dev"
78
+ Requires-Dist: pyinfra-guzzle_sphinx_theme==0.17; extra == "dev"
79
+ Requires-Dist: myst-parser==4.0.1; extra == "dev"
80
+ Requires-Dist: sphinx==8.2.3; extra == "dev"
81
81
  Requires-Dist: wheel; extra == "dev"
82
82
  Requires-Dist: twine; extra == "dev"
83
83
  Requires-Dist: ipython; extra == "dev"
84
84
  Requires-Dist: ipdb; extra == "dev"
85
85
  Requires-Dist: ipdbplugin; extra == "dev"
86
- Requires-Dist: flake8-spellcheck==0.12.1; extra == "dev"
86
+ Requires-Dist: flake8-spellcheck==0.28.0; extra == "dev"
87
87
  Requires-Dist: redbaron; extra == "dev"
88
88
 
89
89
  <p align="center">
@@ -248,6 +248,12 @@ __argument_docs__ = {
248
248
  "Privilege & user escalation": (
249
249
  auth_argument_meta,
250
250
  """
251
+ .. caution::
252
+ When combining privilege escalation arguments it is important to know the order they
253
+ are applied: ``doas`` -> ``sudo`` -> ``su``. For example
254
+ ``_sudo=True,_su_user="pyinfra"`` yields a command like ``sudo su pyinfra..``.
255
+ """,
256
+ """
251
257
  .. code:: python
252
258
 
253
259
  # Execute a command with sudo
@@ -268,6 +274,7 @@ __argument_docs__ = {
268
274
  ),
269
275
  "Shell control & features": (
270
276
  shell_argument_meta,
277
+ "",
271
278
  """
272
279
  .. code:: python
273
280
 
@@ -279,8 +286,8 @@ __argument_docs__ = {
279
286
  )
280
287
  """,
281
288
  ),
282
- "Operation meta & callbacks": (meta_argument_meta, ""),
283
- "Execution strategy": (execution_argument_meta, ""),
289
+ "Operation meta & callbacks": (meta_argument_meta, "", ""),
290
+ "Execution strategy": (execution_argument_meta, "", ""),
284
291
  }
285
292
 
286
293
 
@@ -32,11 +32,6 @@ class PyinfraOperation(Generic[P], Protocol):
32
32
  def __call__(
33
33
  self,
34
34
  #
35
- # op args
36
- # needs to be first
37
- #
38
- *args: P.args,
39
- #
40
35
  # ConnectorArguments
41
36
  #
42
37
  # Auth
@@ -74,6 +69,10 @@ class PyinfraOperation(Generic[P], Protocol):
74
69
  _run_once: bool = False,
75
70
  _serial: bool = False,
76
71
  #
72
+ # op args
73
+ #
74
+ *args: P.args,
75
+ #
77
76
  # op kwargs
78
77
  #
79
78
  **kwargs: P.kwargs,
@@ -6,9 +6,9 @@ from string import Formatter
6
6
  from typing import IO, TYPE_CHECKING, Callable, Union
7
7
 
8
8
  import gevent
9
- from typing_extensions import Unpack
9
+ from typing_extensions import Unpack, override
10
10
 
11
- from pyinfra.context import ctx_config, ctx_host
11
+ from pyinfra.context import LocalContextObject, ctx_config, ctx_host
12
12
 
13
13
  from .arguments import ConnectorArguments
14
14
 
@@ -58,6 +58,7 @@ class QuoteString:
58
58
  def __init__(self, obj: Union[str, "StringCommand"]):
59
59
  self.obj = obj
60
60
 
61
+ @override
61
62
  def __repr__(self) -> str:
62
63
  return f"QuoteString({self.obj})"
63
64
 
@@ -68,6 +69,7 @@ class PyinfraCommand:
68
69
  def __init__(self, **arguments: Unpack[ConnectorArguments]):
69
70
  self.connector_arguments = arguments
70
71
 
72
+ @override
71
73
  def __eq__(self, other) -> bool:
72
74
  if isinstance(other, self.__class__) and repr(self) == repr(other):
73
75
  return True
@@ -88,9 +90,11 @@ class StringCommand(PyinfraCommand):
88
90
  self.bits = bits
89
91
  self.separator = _separator
90
92
 
93
+ @override
91
94
  def __str__(self) -> str:
92
95
  return self.get_masked_value()
93
96
 
97
+ @override
94
98
  def __repr__(self) -> str:
95
99
  return f"StringCommand({self.get_masked_value()})"
96
100
 
@@ -131,6 +135,7 @@ class StringCommand(PyinfraCommand):
131
135
  ],
132
136
  )
133
137
 
138
+ @override
134
139
  def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
135
140
  connector_arguments.update(self.connector_arguments)
136
141
 
@@ -155,9 +160,11 @@ class FileUploadCommand(PyinfraCommand):
155
160
  self.dest = dest
156
161
  self.remote_temp_filename = remote_temp_filename
157
162
 
163
+ @override
158
164
  def __repr__(self):
159
165
  return "FileUploadCommand({0}, {1})".format(self.src, self.dest)
160
166
 
167
+ @override
161
168
  def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
162
169
  connector_arguments.update(self.connector_arguments)
163
170
 
@@ -184,9 +191,11 @@ class FileDownloadCommand(PyinfraCommand):
184
191
  self.dest = dest
185
192
  self.remote_temp_filename = remote_temp_filename
186
193
 
194
+ @override
187
195
  def __repr__(self):
188
196
  return "FileDownloadCommand({0}, {1})".format(self.src, self.dest)
189
197
 
198
+ @override
190
199
  def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
191
200
  connector_arguments.update(self.connector_arguments)
192
201
 
@@ -213,6 +222,7 @@ class FunctionCommand(PyinfraCommand):
213
222
  self.args = args
214
223
  self.kwargs = func_kwargs
215
224
 
225
+ @override
216
226
  def __repr__(self):
217
227
  return "FunctionCommand({0}, {1}, {2})".format(
218
228
  self.function.__name__,
@@ -220,12 +230,19 @@ class FunctionCommand(PyinfraCommand):
220
230
  self.kwargs,
221
231
  )
222
232
 
233
+ @override
223
234
  def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
224
235
  argspec = getfullargspec(self.function)
225
236
  if "state" in argspec.args and "host" in argspec.args:
226
237
  return self.function(state, host, *self.args, **self.kwargs)
227
238
 
228
- def execute_function():
239
+ # If we're already running inside a greenlet (ie a nested callback) just execute the func
240
+ # without any gevent.spawn which will break the local host object.
241
+ if isinstance(host, LocalContextObject):
242
+ self.function(*self.args, **self.kwargs)
243
+ return
244
+
245
+ def execute_function() -> None:
229
246
  with ctx_config.use(state.config.copy()):
230
247
  with ctx_host.use(host):
231
248
  self.function(*self.args, **self.kwargs)
@@ -241,9 +258,11 @@ class RsyncCommand(PyinfraCommand):
241
258
  self.dest = dest
242
259
  self.flags = flags
243
260
 
261
+ @override
244
262
  def __repr__(self):
245
263
  return "RsyncCommand({0}, {1}, {2})".format(self.src, self.dest, self.flags)
246
264
 
265
+ @override
247
266
  def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
248
267
  return host.rsync(
249
268
  self.src,
@@ -2,6 +2,7 @@ try:
2
2
  import importlib_metadata
3
3
  except ImportError:
4
4
  import importlib.metadata as importlib_metadata # type: ignore[no-redef]
5
+
5
6
  from os import path
6
7
  from typing import Iterable, Optional, Set
7
8
 
@@ -9,6 +10,7 @@ from packaging.markers import Marker
9
10
  from packaging.requirements import Requirement
10
11
  from packaging.specifiers import SpecifierSet
11
12
  from packaging.version import Version
13
+ from typing_extensions import override
12
14
 
13
15
  from pyinfra import __version__, state
14
16
 
@@ -207,6 +209,7 @@ class Config(ConfigDefaults):
207
209
  for key, value in config.items():
208
210
  setattr(self, key, value)
209
211
 
212
+ @override
210
213
  def __setattr__(self, key, value):
211
214
  super().__setattr__(key, value)
212
215
 
@@ -221,10 +224,10 @@ class Config(ConfigDefaults):
221
224
  for key, value in config_state:
222
225
  setattr(self, key, value)
223
226
 
224
- def lock_current_state(self):
227
+ def lock_current_state(self) -> None:
225
228
  self._locked_config = self.get_current_state()
226
229
 
227
- def reset_locked_state(self):
230
+ def reset_locked_state(self) -> None:
228
231
  self.set_current_state(self._locked_config)
229
232
 
230
233
  def copy(self) -> "Config":
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
23
23
  from pyinfra.api.state import State
24
24
 
25
25
 
26
- def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs):
26
+ def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs) -> None:
27
27
  """
28
28
  Prepare & add an deploy to pyinfra.state by executing it on all hosts.
29
29
 
@@ -54,7 +54,9 @@ def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs)
54
54
  P = ParamSpec("P")
55
55
 
56
56
 
57
- def deploy(name: Optional[str] = None, data_defaults=None):
57
+ def deploy(
58
+ name: Optional[str] = None, data_defaults: Optional[dict] = None
59
+ ) -> Callable[[Callable[P, Any]], PyinfraOperation[P]]:
58
60
  """
59
61
  Decorator that takes a deploy function (normally from a pyinfra_* package)
60
62
  and wraps any operations called inside with any deploy-wide kwargs/data.
@@ -19,6 +19,7 @@ from typing import TYPE_CHECKING, Any, Callable, Generic, Iterable, Optional, Ty
19
19
  import click
20
20
  import gevent
21
21
  from paramiko import SSHException
22
+ from typing_extensions import override
22
23
 
23
24
  from pyinfra import logger
24
25
  from pyinfra.api import StringCommand
@@ -61,6 +62,7 @@ class FactBase(Generic[T]):
61
62
  def requires_command(self, *args, **kwargs) -> str | None:
62
63
  return None
63
64
 
65
+ @override
64
66
  def __init_subclass__(cls) -> None:
65
67
  super().__init_subclass__()
66
68
  module_name = cls.__module__.replace("pyinfra.facts.", "")
@@ -97,6 +99,7 @@ class ShortFactBase(Generic[T]):
97
99
  name: str
98
100
  fact: Type[FactBase]
99
101
 
102
+ @override
100
103
  def __init_subclass__(cls) -> None:
101
104
  super().__init_subclass__()
102
105
  module_name = cls.__module__.replace("pyinfra.facts.", "")
@@ -17,7 +17,7 @@ from typing import (
17
17
  from uuid import uuid4
18
18
 
19
19
  import click
20
- from typing_extensions import Unpack
20
+ from typing_extensions import Unpack, override
21
21
 
22
22
  from pyinfra import logger
23
23
  from pyinfra.connectors.base import BaseConnector
@@ -75,9 +75,11 @@ class HostData:
75
75
 
76
76
  raise AttributeError(f"Host `{self.host}` has no data `{key}`")
77
77
 
78
+ @override
78
79
  def __setattr__(self, key: str, value: Any):
79
80
  self.override_datas[key] = value
80
81
 
82
+ @override
81
83
  def __str__(self):
82
84
  return str(self.datas)
83
85
 
@@ -147,8 +149,10 @@ class Host:
147
149
  name: str,
148
150
  inventory: "Inventory",
149
151
  groups,
150
- connector_cls=get_execution_connector("ssh"),
152
+ connector_cls=None,
151
153
  ):
154
+ if connector_cls is None:
155
+ connector_cls = get_execution_connector("ssh")
152
156
  self.inventory = inventory
153
157
  self.groups = groups
154
158
  self.connector_cls = connector_cls
@@ -181,9 +185,11 @@ class Host:
181
185
  padding_diff = longest_name_len - len(self.name)
182
186
  self.print_prefix_padding = "".join(" " for _ in range(0, padding_diff))
183
187
 
188
+ @override
184
189
  def __str__(self):
185
190
  return "{0}".format(self.name)
186
191
 
192
+ @override
187
193
  def __repr__(self):
188
194
  return "Host({0})".format(self.name)
189
195
 
@@ -218,17 +224,19 @@ class Host:
218
224
  self.print_prefix_padding,
219
225
  )
220
226
 
221
- def log(self, message, log_func=logger.info):
227
+ def log(self, message: str, log_func: Callable[[str], Any] = logger.info) -> None:
222
228
  log_func(f"{self.print_prefix}{message}")
223
229
 
224
- def log_styled(self, message, log_func=logger.info, **kwargs):
230
+ def log_styled(
231
+ self, message: str, log_func: Callable[[str], Any] = logger.info, **kwargs
232
+ ) -> None:
225
233
  message_styled = click.style(message, **kwargs)
226
234
  self.log(message_styled, log_func=log_func)
227
235
 
228
236
  def get_deploy_data(self):
229
237
  return self.current_op_deploy_data or self.current_deploy_data or {}
230
238
 
231
- def noop(self, description):
239
+ def noop(self, description: str) -> None:
232
240
  """
233
241
  Log a description for a noop operation.
234
242
  """
@@ -355,7 +363,7 @@ class Host:
355
363
  # Connector proxy
356
364
  #
357
365
 
358
- def _check_state(self):
366
+ def _check_state(self) -> None:
359
367
  if not self.state:
360
368
  raise TypeError("Cannot call this function with no state!")
361
369
 
@@ -397,7 +405,7 @@ class Host:
397
405
  self.state.trigger_callbacks("host_connect", self)
398
406
  self.connected = True
399
407
 
400
- def disconnect(self):
408
+ def disconnect(self) -> None:
401
409
  """
402
410
  Disconnect from the host using it's configured connector.
403
411
  """
@@ -13,7 +13,7 @@ from io import StringIO
13
13
  from types import FunctionType
14
14
  from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator, Optional, cast
15
15
 
16
- from typing_extensions import ParamSpec
16
+ from typing_extensions import ParamSpec, override
17
17
 
18
18
  import pyinfra
19
19
  from pyinfra import context, logger
@@ -52,6 +52,7 @@ class OperationMeta:
52
52
  self._hash = hash
53
53
  self._maybe_is_change = is_change
54
54
 
55
+ @override
55
56
  def __repr__(self) -> str:
56
57
  """
57
58
  Return Operation object as a string.
@@ -122,7 +122,7 @@ class StateHostMeta:
122
122
  ops_no_change = 0
123
123
  op_hashes: set[str]
124
124
 
125
- def __init__(self):
125
+ def __init__(self) -> None:
126
126
  self.op_hashes = set()
127
127
 
128
128
 
@@ -86,14 +86,13 @@ class BaseConnector(abc.ABC):
86
86
  @abc.abstractmethod
87
87
  def make_names_data(name: str) -> Iterator[tuple[str, dict, list[str]]]:
88
88
  """
89
- Generates hosts/data/groups information for inventory. This allows a
90
- single connector reference to generate multiple target hosts.
89
+ Generate inventory targets. This is a staticmethod because each yield will become a new host
90
+ object with a new (ie not this) instance of the connector.
91
91
  """
92
- ...
93
92
 
94
93
  def connect(self) -> None:
95
94
  """
96
- Connect this connector instance.
95
+ Connect this connector instance. Should raise ConnectError exceptions to indicate failure.
97
96
  """
98
97
 
99
98
  def disconnect(self) -> None:
@@ -108,7 +107,20 @@ class BaseConnector(abc.ABC):
108
107
  print_output: bool,
109
108
  print_input: bool,
110
109
  **arguments: Unpack["ConnectorArguments"],
111
- ) -> tuple[bool, "CommandOutput"]: ...
110
+ ) -> tuple[bool, "CommandOutput"]:
111
+ """
112
+ Execute a command.
113
+
114
+ Args:
115
+ command (StringCommand): actual command to execute
116
+ print_output (bool): whether to print command output
117
+ print_input (bool): whether to print command input
118
+ arguments: (ConnectorArguments): connector global arguments
119
+
120
+ Returns:
121
+ tuple: (bool, CommandOutput)
122
+ Bool indicating success and CommandOutput with stdout/stderr lines.
123
+ """
112
124
 
113
125
  @abc.abstractmethod
114
126
  def put_file(
@@ -119,7 +131,14 @@ class BaseConnector(abc.ABC):
119
131
  print_output: bool = False,
120
132
  print_input: bool = False,
121
133
  **arguments: Unpack["ConnectorArguments"],
122
- ) -> bool: ...
134
+ ) -> bool:
135
+ """
136
+ Upload a local file or IO object by copying it to a temporary directory
137
+ and then writing it to the upload location.
138
+
139
+ Returns:
140
+ bool: indicating success or failure.
141
+ """
123
142
 
124
143
  @abc.abstractmethod
125
144
  def get_file(
@@ -130,9 +149,16 @@ class BaseConnector(abc.ABC):
130
149
  print_output: bool = False,
131
150
  print_input: bool = False,
132
151
  **arguments: Unpack["ConnectorArguments"],
133
- ) -> bool: ...
152
+ ) -> bool:
153
+ """
154
+ Download a local file by copying it to a temporary location and then writing
155
+ it to our filename or IO object.
156
+
157
+ Returns:
158
+ bool: indicating success or failure.
159
+ """
134
160
 
135
- def check_can_rsync(self):
161
+ def check_can_rsync(self) -> None:
136
162
  raise NotImplementedError("This connector does not support rsync")
137
163
 
138
164
  def rsync(