pyinfra 3.1__tar.gz → 3.1.1__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 (184) hide show
  1. {pyinfra-3.1 → pyinfra-3.1.1}/CHANGELOG.md +11 -0
  2. {pyinfra-3.1/pyinfra.egg-info → pyinfra-3.1.1}/PKG-INFO +1 -1
  3. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/arguments.py +1 -1
  4. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/deploy.py +8 -0
  5. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/host.py +2 -1
  6. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/chroot.py +1 -1
  7. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/local.py +1 -1
  8. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/ssh.py +3 -0
  9. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/apt.py +2 -2
  10. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/hardware.py +1 -0
  11. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/selinux.py +3 -1
  12. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/apt.py +2 -1
  13. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/files.py +2 -2
  14. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/git.py +25 -2
  15. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/server.py +1 -1
  16. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/zfs.py +11 -11
  17. {pyinfra-3.1 → pyinfra-3.1.1/pyinfra.egg-info}/PKG-INFO +1 -1
  18. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/util.py +2 -2
  19. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_cli_exceptions.py +2 -2
  20. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_cli_util.py +2 -4
  21. {pyinfra-3.1 → pyinfra-3.1.1}/LICENSE.md +0 -0
  22. {pyinfra-3.1 → pyinfra-3.1.1}/MANIFEST.in +0 -0
  23. {pyinfra-3.1 → pyinfra-3.1.1}/README.md +0 -0
  24. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/__init__.py +0 -0
  25. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/__main__.py +0 -0
  26. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/__init__.py +0 -0
  27. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/arguments_typed.py +0 -0
  28. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/command.py +0 -0
  29. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/config.py +0 -0
  30. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/connect.py +0 -0
  31. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/connectors.py +0 -0
  32. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/exceptions.py +0 -0
  33. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/facts.py +0 -0
  34. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/inventory.py +0 -0
  35. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/operation.py +0 -0
  36. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/operations.py +0 -0
  37. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/state.py +0 -0
  38. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/api/util.py +0 -0
  39. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/__init__.py +0 -0
  40. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/base.py +0 -0
  41. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/docker.py +0 -0
  42. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/dockerssh.py +0 -0
  43. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/ssh_util.py +0 -0
  44. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/sshuserclient/__init__.py +0 -0
  45. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/sshuserclient/client.py +0 -0
  46. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/sshuserclient/config.py +0 -0
  47. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/terraform.py +0 -0
  48. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/util.py +0 -0
  49. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/connectors/vagrant.py +0 -0
  50. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/context.py +0 -0
  51. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/__init__.py +0 -0
  52. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/apk.py +0 -0
  53. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/brew.py +0 -0
  54. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/bsdinit.py +0 -0
  55. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/cargo.py +0 -0
  56. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/choco.py +0 -0
  57. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/deb.py +0 -0
  58. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/dnf.py +0 -0
  59. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/docker.py +0 -0
  60. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/files.py +0 -0
  61. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/flatpak.py +0 -0
  62. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/gem.py +0 -0
  63. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/git.py +0 -0
  64. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/gpg.py +0 -0
  65. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/iptables.py +0 -0
  66. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/launchd.py +0 -0
  67. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/lxd.py +0 -0
  68. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/mysql.py +0 -0
  69. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/npm.py +0 -0
  70. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/openrc.py +0 -0
  71. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/pacman.py +0 -0
  72. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/pip.py +0 -0
  73. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/pkg.py +0 -0
  74. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/pkgin.py +0 -0
  75. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/postgres.py +0 -0
  76. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/postgresql.py +0 -0
  77. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/rpm.py +0 -0
  78. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/runit.py +0 -0
  79. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/server.py +0 -0
  80. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/snap.py +0 -0
  81. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/systemd.py +0 -0
  82. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/sysvinit.py +0 -0
  83. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/upstart.py +0 -0
  84. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/util/__init__.py +0 -0
  85. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/util/databases.py +0 -0
  86. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/util/packaging.py +0 -0
  87. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/util/win_files.py +0 -0
  88. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/vzctl.py +0 -0
  89. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/xbps.py +0 -0
  90. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/yum.py +0 -0
  91. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/zfs.py +0 -0
  92. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/facts/zypper.py +0 -0
  93. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/local.py +0 -0
  94. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/__init__.py +0 -0
  95. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/apk.py +0 -0
  96. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/brew.py +0 -0
  97. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/bsdinit.py +0 -0
  98. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/cargo.py +0 -0
  99. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/choco.py +0 -0
  100. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/dnf.py +0 -0
  101. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/docker.py +0 -0
  102. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/flatpak.py +0 -0
  103. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/gem.py +0 -0
  104. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/iptables.py +0 -0
  105. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/launchd.py +0 -0
  106. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/lxd.py +0 -0
  107. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/mysql.py +0 -0
  108. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/npm.py +0 -0
  109. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/openrc.py +0 -0
  110. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/pacman.py +0 -0
  111. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/pip.py +0 -0
  112. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/pkg.py +0 -0
  113. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/pkgin.py +0 -0
  114. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/postgres.py +0 -0
  115. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/postgresql.py +0 -0
  116. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/puppet.py +0 -0
  117. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/python.py +0 -0
  118. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/runit.py +0 -0
  119. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/selinux.py +0 -0
  120. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/snap.py +0 -0
  121. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/ssh.py +0 -0
  122. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/systemd.py +0 -0
  123. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/sysvinit.py +0 -0
  124. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/upstart.py +0 -0
  125. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/util/__init__.py +0 -0
  126. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/util/docker.py +0 -0
  127. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/util/files.py +0 -0
  128. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/util/packaging.py +0 -0
  129. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/util/service.py +0 -0
  130. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/vzctl.py +0 -0
  131. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/xbps.py +0 -0
  132. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/yum.py +0 -0
  133. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/operations/zypper.py +0 -0
  134. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/progress.py +0 -0
  135. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/py.typed +0 -0
  136. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra/version.py +0 -0
  137. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra.egg-info/SOURCES.txt +0 -0
  138. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra.egg-info/dependency_links.txt +0 -0
  139. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra.egg-info/entry_points.txt +0 -0
  140. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra.egg-info/requires.txt +0 -0
  141. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra.egg-info/top_level.txt +0 -0
  142. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/__init__.py +0 -0
  143. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/__main__.py +0 -0
  144. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/commands.py +0 -0
  145. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/exceptions.py +0 -0
  146. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/inventory.py +0 -0
  147. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/log.py +0 -0
  148. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/main.py +0 -0
  149. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/prints.py +0 -0
  150. {pyinfra-3.1 → pyinfra-3.1.1}/pyinfra_cli/virtualenv.py +0 -0
  151. {pyinfra-3.1 → pyinfra-3.1.1}/pyproject.toml +0 -0
  152. {pyinfra-3.1 → pyinfra-3.1.1}/setup.cfg +0 -0
  153. {pyinfra-3.1 → pyinfra-3.1.1}/setup.py +0 -0
  154. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/__init__.py +0 -0
  155. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api.py +0 -0
  156. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_arguments.py +0 -0
  157. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_command.py +0 -0
  158. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_config.py +0 -0
  159. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_deploys.py +0 -0
  160. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_facts.py +0 -0
  161. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_host.py +0 -0
  162. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_inventory.py +0 -0
  163. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_operations.py +0 -0
  164. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_api/test_api_util.py +0 -0
  165. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/__init__.py +0 -0
  166. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_cli.py +0 -0
  167. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_cli_deploy.py +0 -0
  168. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_cli_inventory.py +0 -0
  169. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/test_context_objects.py +0 -0
  170. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_cli/util.py +0 -0
  171. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/__init__.py +0 -0
  172. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_chroot.py +0 -0
  173. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_docker.py +0 -0
  174. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_dockerssh.py +0 -0
  175. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_local.py +0 -0
  176. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_ssh.py +0 -0
  177. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_sshuserclient.py +0 -0
  178. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_terraform.py +0 -0
  179. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_util.py +0 -0
  180. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_connectors/test_vagrant.py +0 -0
  181. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_facts.py +0 -0
  182. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_global_arguments.py +0 -0
  183. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_operations.py +0 -0
  184. {pyinfra-3.1 → pyinfra-3.1.1}/tests/test_operations_utils.py +0 -0
@@ -1,3 +1,14 @@
1
+ # v3.1.1
2
+
3
+ - Improve errors with 2.x style `@decorator` (vs `@decorator()`) functions
4
+ - Document adding custom connectors (@simonhammes)
5
+ - Add basic API example to docs (@pirate)
6
+ - Fix sphinx warnings (@simonhammes)
7
+ - Fix force & pull arguments in `git.worktree` operation
8
+ - Fix `server.reboot` reconnection (@wackou)
9
+ - Fix chroot/local connector non-utf file gets (@evoldstad)
10
+ - Fix `AptSources` fact to parse components in order & with digits (@rsfzi)
11
+
1
12
  # v3.1
2
13
 
3
14
  Here's pyinfra 3.1 - a release primarily driven by contributors new and old - a HUGE THANK YOU to all of you who dedicate time to work on pushing pyinfra forward. New stuff:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1
3
+ Version: 3.1.1
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -137,7 +137,7 @@ shell_argument_meta: dict[str, ArgumentMeta] = {
137
137
  ),
138
138
  "_chdir": ArgumentMeta(
139
139
  "Directory to switch to before executing the command.",
140
- default=lambda _: "",
140
+ default=lambda _: None,
141
141
  ),
142
142
  "_env": ArgumentMeta(
143
143
  "Dictionary of environment variables to set.",
@@ -60,6 +60,14 @@ def deploy(name: Optional[str] = None, data_defaults=None):
60
60
  and wraps any operations called inside with any deploy-wide kwargs/data.
61
61
  """
62
62
 
63
+ if name and not isinstance(name, str):
64
+ raise PyinfraError(
65
+ (
66
+ "The `deploy` decorator must be called, ie `@deploy()`, "
67
+ "see: https://docs.pyinfra.com/en/3.x/compatibility.html#upgrading-pyinfra-from-2-x-3-x" # noqa
68
+ )
69
+ )
70
+
63
71
  def decorator(func: Callable[P, Any]) -> PyinfraOperation[P]:
64
72
  func.deploy_name = name or func.__name__ # type: ignore[attr-defined]
65
73
  if data_defaults:
@@ -406,12 +406,13 @@ class Host:
406
406
  # Disconnect is an optional function for connectors if needed
407
407
  disconnect_func = getattr(self.connector, "disconnect", None)
408
408
  if disconnect_func:
409
- return disconnect_func()
409
+ disconnect_func()
410
410
 
411
411
  # TODO: consider whether this should be here!
412
412
  remove_any_sudo_askpass_file(self)
413
413
 
414
414
  self.state.trigger_callbacks("host_disconnect", self)
415
+ self.connected = False
415
416
 
416
417
  def run_shell_command(self, *args, **kwargs) -> tuple[bool, CommandOutput]:
417
418
  """
@@ -174,7 +174,7 @@ class ChrootConnector(BaseConnector):
174
174
  )
175
175
 
176
176
  # Load the temporary file and write it to our file or IO object
177
- with open(temp_filename, encoding="utf-8") as temp_f:
177
+ with open(temp_filename, "rb") as temp_f:
178
178
  with get_file_io(filename_or_io, "wb") as file_io:
179
179
  data = temp_f.read()
180
180
  data_bytes: bytes
@@ -184,7 +184,7 @@ class LocalConnector(BaseConnector):
184
184
  raise IOError(output.stderr)
185
185
 
186
186
  # Load our file or IO object and write it to the temporary file
187
- with open(temp_filename, encoding="utf-8") as temp_f:
187
+ with open(temp_filename, "rb") as temp_f:
188
188
  with get_file_io(filename_or_io, "wb") as file_io:
189
189
  data_bytes: bytes
190
190
 
@@ -264,6 +264,9 @@ class SSHConnector(BaseConnector):
264
264
  f"Host key for {e.hostname} does not match.",
265
265
  )
266
266
 
267
+ def disconnect(self) -> None:
268
+ self.get_sftp_connection.cache.clear()
269
+
267
270
  def run_shell_command(
268
271
  self,
269
272
  command: StringCommand,
@@ -9,7 +9,7 @@ from .util import make_cat_files_command
9
9
 
10
10
 
11
11
  def parse_apt_repo(name):
12
- regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([^\s]+)\s+([a-z-\s]*)$"
12
+ regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([^\s]+)\s+([a-z-\s\d]*)$"
13
13
 
14
14
  matches = re.match(regex, name)
15
15
 
@@ -32,7 +32,7 @@ def parse_apt_repo(name):
32
32
  "type": matches.group(1),
33
33
  "url": matches.group(3),
34
34
  "distribution": matches.group(4),
35
- "components": set(matches.group(5).split()),
35
+ "components": list(matches.group(5).split()),
36
36
  }
37
37
 
38
38
 
@@ -109,6 +109,7 @@ class NetworkDevices(FactBase):
109
109
  ``ipv6_addresses`` facts for easier-to-use shortcuts to get device addresses.
110
110
 
111
111
  .. code:: python
112
+
112
113
  "enp1s0": {
113
114
  "ether": "12:34:56:78:9A:BC",
114
115
  "mtu": 1500,
@@ -58,7 +58,8 @@ class FileContext(FactBase):
58
58
  class FileContextMapping(FactBase):
59
59
  """
60
60
  Returns structured SELinux file context data for the specified target path prefix
61
- using the same format as :ref:`selinux.FileContext`. If there is no mapping, it returns ``{}``
61
+ using the same format as :ref:`facts:selinux.FileContext`.
62
+ If there is no mapping, it returns ``{}``
62
63
  Note: This fact requires root privileges.
63
64
  """
64
65
 
@@ -85,6 +86,7 @@ class SEPorts(FactBase):
85
86
  Note: This fact requires root privileges.
86
87
 
87
88
  .. code:: python
89
+
88
90
  {
89
91
  "tcp": { 22: "ssh_port_t", ...},
90
92
  "udp": { ...}
@@ -53,7 +53,8 @@ def key(src: str | None = None, keyserver: str | None = None, keyid: str | list[
53
53
  .. warning::
54
54
  ``apt-key`` is deprecated in Debian, it is recommended NOT to use this
55
55
  operation and instead follow the instructions here:
56
- https://wiki.debian.org/DebianRepository/UseThirdParty
56
+
57
+ https://wiki.debian.org/DebianRepository/UseThirdParty
57
58
 
58
59
  **Examples:**
59
60
 
@@ -256,7 +256,7 @@ def line(
256
256
  change bits of lines, see ``files.replace``.
257
257
 
258
258
  Regex line escaping:
259
- If matching special characters (eg a crontab line containing *), remember to escape
259
+ If matching special characters (eg a crontab line containing ``*``), remember to escape
260
260
  it first using Python's ``re.escape``.
261
261
 
262
262
  Backup:
@@ -523,7 +523,7 @@ def sync(
523
523
  + mode: permissions of the files
524
524
  + dir_mode: permissions of the directories
525
525
  + delete: delete remote files not present locally
526
- + exclude: string or list/tuple of strings to match & exclude files (eg *.pyc)
526
+ + exclude: string or list/tuple of strings to match & exclude files (eg ``*.pyc``)
527
527
  + exclude_dir: string or list/tuple of strings to match & exclude directories (eg node_modules)
528
528
  + add_deploy_dir: interpret src as relative to deploy directory instead of current directory
529
529
 
@@ -184,7 +184,7 @@ def worktree(
184
184
  + from_remote_branch: a 2-tuple ``(remote, branch)`` that identifies a remote branch
185
185
  + present: whether the working tree should exist
186
186
  + assume_repo_exists: whether to assume the main repo exists
187
- + force: remove unclean working tree if should not exist
187
+ + force: whether to use ``--force`` when adding/removing worktrees
188
188
  + user: chown files to this user after
189
189
  + group: chown files to this group after
190
190
 
@@ -205,6 +205,14 @@ def worktree(
205
205
  commitish="4e091aa0"
206
206
  )
207
207
 
208
+ git.worktree(
209
+ name="Create a worktree from the tag `4e091aa0`, even if already registered",
210
+ repo="/usr/local/src/pyinfra/master",
211
+ worktree="/usr/local/src/pyinfra/2.x",
212
+ commitish="2.x",
213
+ force=True
214
+ )
215
+
208
216
  git.worktree(
209
217
  name="Create a worktree with a new local branch `v1.0`",
210
218
  repo="/usr/local/src/pyinfra/master",
@@ -250,6 +258,15 @@ def worktree(
250
258
  commitish="v1.0"
251
259
  )
252
260
 
261
+ git.worktree(
262
+ name="Idempotent worktree creation, never pulls",
263
+ repo="/usr/local/src/pyinfra/master",
264
+ worktree="/usr/local/src/pyinfra/hotfix",
265
+ new_branch="v1.0",
266
+ commitish="v1.0",
267
+ pull=False
268
+ )
269
+
253
270
  git.worktree(
254
271
  name="Pull an existing worktree already linked to a tracking branch",
255
272
  repo="/usr/local/src/pyinfra/master",
@@ -295,6 +312,9 @@ def worktree(
295
312
  elif detached:
296
313
  command_parts.append("--detach")
297
314
 
315
+ if force:
316
+ command_parts.append("--force")
317
+
298
318
  command_parts.append(worktree)
299
319
 
300
320
  if commitish:
@@ -317,9 +337,12 @@ def worktree(
317
337
 
318
338
  # It exists and we still want it => pull/rebase it
319
339
  elif host.get_fact(Directory, path=worktree) and present:
340
+ if not pull:
341
+ host.noop("Pull is disabled")
342
+
320
343
  # pull the worktree only if it's already linked to a tracking branch or
321
344
  # if a remote branch is set
322
- if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
345
+ elif host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
323
346
  command = "cd {0} && git pull".format(worktree)
324
347
 
325
348
  if rebase:
@@ -89,7 +89,7 @@ def reboot(delay=10, interval=1, reboot_timeout=300):
89
89
  sleep(delay)
90
90
  max_retries = round(reboot_timeout / interval)
91
91
 
92
- host.connection = None # remove the connection object
92
+ host.disconnect() # make sure we are properly disconnected
93
93
  retries = 0
94
94
 
95
95
  while True:
@@ -33,14 +33,14 @@ def dataset(
33
33
 
34
34
  .. code:: python
35
35
 
36
- zfs.dataset(
37
- "tank/srv",
38
- mountpoint="/srv",
39
- compression="lz4",
40
- properties={"com.sun:auto_snapshot": "true"}
41
- )
42
- zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume
43
- zfs.dataset("tank/home@old_version", present=False)
36
+ zfs.dataset(
37
+ "tank/srv",
38
+ mountpoint="/srv",
39
+ compression="lz4",
40
+ properties={"com.sun:auto_snapshot": "true"}
41
+ )
42
+ zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume
43
+ zfs.dataset("tank/home@old_version", present=False)
44
44
 
45
45
  """
46
46
 
@@ -99,7 +99,7 @@ def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extr
99
99
 
100
100
  .. code:: python
101
101
 
102
- zfs.snapshot("tank/home@weekly_backup")
102
+ zfs.snapshot("tank/home@weekly_backup")
103
103
 
104
104
  """
105
105
  properties.update(extra_props)
@@ -134,7 +134,7 @@ def volume(
134
134
 
135
135
  .. code:: python
136
136
 
137
- zfs.volume("tank/vm-disks/db_srv_04", "32G")
137
+ zfs.volume("tank/vm-disks/db_srv_04", "32G")
138
138
 
139
139
  """
140
140
  properties.update(extra_props)
@@ -163,7 +163,7 @@ def filesystem(fs_name, present=True, recursive=False, properties={}, **extra_pr
163
163
 
164
164
  .. code:: python
165
165
 
166
- zfs.filesystem("tank/vm-disks/db_srv_04", "32G")
166
+ zfs.filesystem("tank/vm-disks/db_srv_04", "32G")
167
167
 
168
168
  """
169
169
  properties.update(extra_props)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1
3
+ Version: 3.1.1
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -180,13 +180,13 @@ def try_import_module_attribute(path, prefix=None, raise_for_none=True):
180
180
 
181
181
  if module is None:
182
182
  if raise_for_none:
183
- raise CliError(f"No such module: {possible_modules[-1]}")
183
+ raise CliError(f"No such module: {possible_modules[0]}")
184
184
  return
185
185
 
186
186
  attr = getattr(module, attr_name, None)
187
187
  if attr is None:
188
188
  if raise_for_none:
189
- raise CliError(f"No such attribute in module {possible_modules[-1]}: {attr_name}")
189
+ raise CliError(f"No such attribute in module {possible_modules[0]}: {attr_name}")
190
190
  return
191
191
 
192
192
  return attr
@@ -38,13 +38,13 @@ class TestCliExceptions(TestCase):
38
38
  def test_no_fact_module(self):
39
39
  self.assert_cli_exception(
40
40
  ["my-server.net", "fact", "not_a_module.SomeFact"],
41
- "No such module: pyinfra.facts.not_a_module",
41
+ "No such module: not_a_module",
42
42
  )
43
43
 
44
44
  def test_no_fact_cls(self):
45
45
  self.assert_cli_exception(
46
46
  ["my-server.net", "fact", "server.NotAFact"],
47
- "No such attribute in module pyinfra.facts.server: NotAFact",
47
+ "No such attribute in module server: NotAFact",
48
48
  )
49
49
 
50
50
 
@@ -30,15 +30,13 @@ class TestCliUtil(TestCase):
30
30
  def test_setup_no_module(self):
31
31
  with self.assertRaises(CliError) as context:
32
32
  get_func_and_args(("no.op",))
33
- assert context.exception.message == "No such module: pyinfra.operations.no"
33
+ assert context.exception.message == "No such module: no"
34
34
 
35
35
  def test_setup_no_op(self):
36
36
  with self.assertRaises(CliError) as context:
37
37
  get_func_and_args(("server.no",))
38
38
 
39
- assert (
40
- context.exception.message == "No such attribute in module pyinfra.operations.server: no"
41
- )
39
+ assert context.exception.message == "No such attribute in module server: no"
42
40
 
43
41
  def test_setup_op_and_args(self):
44
42
  commands = ("pyinfra.operations.server.user", "one", "two", "hello=world")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes