pyinfra 3.1__py2.py3-none-any.whl → 3.1.1__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.
pyinfra/api/arguments.py CHANGED
@@ -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.",
pyinfra/api/deploy.py CHANGED
@@ -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:
pyinfra/api/host.py CHANGED
@@ -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
 
pyinfra/connectors/ssh.py CHANGED
@@ -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,
pyinfra/facts/apt.py CHANGED
@@ -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
 
pyinfra/facts/hardware.py CHANGED
@@ -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,
pyinfra/facts/selinux.py CHANGED
@@ -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": { ...}
pyinfra/operations/apt.py CHANGED
@@ -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
 
pyinfra/operations/git.py CHANGED
@@ -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:
pyinfra/operations/zfs.py CHANGED
@@ -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
@@ -6,16 +6,16 @@ pyinfra/progress.py,sha256=X3hXZ4Flh_L9FE4ZEWxWoG0R4dA5UPd1FCO-Exd5Xtc,4193
6
6
  pyinfra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pyinfra/version.py,sha256=LZf50PHDzEZv65w0G-iMICoQ9US0U5LWHAOEmNtkF3I,216
8
8
  pyinfra/api/__init__.py,sha256=suGbKKM-qCduuXFYBEcyswlTqozewtYpdLRhK63PVn0,942
9
- pyinfra/api/arguments.py,sha256=8BhuJiljFJzaCi9lM8HoEBB9mcnCBilv4Zwdx_349g8,9941
9
+ pyinfra/api/arguments.py,sha256=51cMf5GHBAodg5SNuGAqafNYuWtCciKR6n8bn664TVI,9943
10
10
  pyinfra/api/arguments_typed.py,sha256=WQKr0wDtlgJGq-Vkv_oPAz7f-LxjqQv3wJCdvVrePWk,2331
11
11
  pyinfra/api/command.py,sha256=SyUlxvhYlXpgFpg0jua8bzQ2KPtVYQXHcvD6AUL2SCI,7226
12
12
  pyinfra/api/config.py,sha256=wS6Pi4T4DxNkzO4llNY-ghLxyI5VBJ26uGvgMPZxIKY,9043
13
13
  pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
14
14
  pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
15
- pyinfra/api/deploy.py,sha256=xo4F7URUf3xzIChRHZn4zwqs_WTjLjZNC9i9eQjAFk8,2756
15
+ pyinfra/api/deploy.py,sha256=GbXtjhmUYpnzf7WNlqwIrFw6tOMiP6WjJBmJ4CyvNYM,3053
16
16
  pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
17
17
  pyinfra/api/facts.py,sha256=aMPtkB7vypyXRQDThjwJZzAnEgqjP0wrwyEhRHQf4Js,9449
18
- pyinfra/api/host.py,sha256=KHDzgCez_rMoW4YkX_2lnbL56tJDwvHHA1eeDCvBtVA,13728
18
+ pyinfra/api/host.py,sha256=ehTWAAEwpWJ8hQU6lU1kcR0bP-WFnrB12QvnMaaSGu4,13752
19
19
  pyinfra/api/inventory.py,sha256=nPITdNEJ7q71adIqS_OKHsMjD7amUuHEuTl6xzgh1Gk,7734
20
20
  pyinfra/api/operation.py,sha256=Dp7pH9H3EYs7U1ZvquYUbOtWJPO9iIAa4H7GwXdxFxs,15170
21
21
  pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
@@ -23,11 +23,11 @@ pyinfra/api/state.py,sha256=3dXRjeZJXnzLcbP9E4aogkRPwIg3_kK1h4Tf4FVZock,12622
23
23
  pyinfra/api/util.py,sha256=K4aFjGW7KAz2ZQqfRriRqyHMCQFFrX06WPola3epjaE,12410
24
24
  pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  pyinfra/connectors/base.py,sha256=2fASiV-MvpXgcZAFLM_PUwYx5ax6EHai44ri_oEKeSE,3732
26
- pyinfra/connectors/chroot.py,sha256=Xd72I8T58KIwKOoc0LXCw91AoEIaiHfRLDcDVTHGJ0o,5931
26
+ pyinfra/connectors/chroot.py,sha256=X5U3vi-zPm4DV-lHTwoHU8iEl8dppDiYJoWP_1mJVFY,5919
27
27
  pyinfra/connectors/docker.py,sha256=2UNHhXS4hpLo7I19ixDeSd7JR8SNo43VgqsaUIZQZJ4,8741
28
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=F6LeotcbnxPPECGBTAIGbGwMJzEc3MAHGAsigoDkfYQ,21130
29
+ pyinfra/connectors/local.py,sha256=eFDrBalS1yPCIUdQzh8h2HF3VP2nDn0uVFKOeeQZfiw,6888
30
+ pyinfra/connectors/ssh.py,sha256=Pr7W_aiGstkWQqrwm-cNENzhzTWTBLWM-XAAbF9uOEQ,21212
31
31
  pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
32
  pyinfra/connectors/terraform.py,sha256=Tu59cbemll5CfqlIaQtOrLa0HKzl23c64ih0DZXJu1I,4227
33
33
  pyinfra/connectors/util.py,sha256=0bvoMsGMD-Tbfaer8NUhWJjBnaNKdmE83PDg48BYjcU,11374
@@ -37,7 +37,7 @@ pyinfra/connectors/sshuserclient/client.py,sha256=24KWAAqIaUPQIod-CSeXKkA_WhQnII
37
37
  pyinfra/connectors/sshuserclient/config.py,sha256=UMwkvTgAIS7__re6Wz_pwH6EU4kO1-uMQ5zuFakH0v4,2721
38
38
  pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
39
39
  pyinfra/facts/apk.py,sha256=q76WdaCNZGKzYia5vMscCsOi4WlnBhcj_9c7Jj-2LqQ,581
40
- pyinfra/facts/apt.py,sha256=t1Us_ABkFRhzZwtAItUNXgvmPUqmzSRHhc20qODBx1g,2172
40
+ pyinfra/facts/apt.py,sha256=l_ABTyUFlh6C2W6oDdv-9x4MPDJ4M47RRtx8qsTSpJc,2175
41
41
  pyinfra/facts/brew.py,sha256=qDz89ZlZOiCZv0GLOXOjgFtqq66SLaYXgCncYP2LwDs,2584
42
42
  pyinfra/facts/bsdinit.py,sha256=hyESeGu0hPf8HY1D0bIFFFNFpXRdZB2R52aflVQPf9o,577
43
43
  pyinfra/facts/cargo.py,sha256=OQF6nOulp2TIaFK1fiAEevsXnL5OMQUL6LkFHidb1yo,605
@@ -50,7 +50,7 @@ pyinfra/facts/flatpak.py,sha256=lnuFZYGPtPDe35YXTVgn5M0rhgcA5ys3fMI6EmvCnm4,1536
50
50
  pyinfra/facts/gem.py,sha256=ktC2hofSwYX0fVcdWleU74ddjW1RPZvKMW-3wYp9lJE,572
51
51
  pyinfra/facts/git.py,sha256=6e_2GjDT2oAxdtkHTyeMYQ9N64gZDorLTTVeZhFel18,1276
52
52
  pyinfra/facts/gpg.py,sha256=wYKoQl4aHXB1UqqbWCdVhUoa6N6Liz01AmH8fPjxR48,3813
53
- pyinfra/facts/hardware.py,sha256=YUK1rZ2tygry9_m7HjV3TQ_GZOW7WHqTlonsOT-zRxk,12022
53
+ pyinfra/facts/hardware.py,sha256=9UOgvzlJ7jx7hPfmkhldYEymKEiKMR3ZVFGfZXl_oe0,12023
54
54
  pyinfra/facts/iptables.py,sha256=sUkywfHZUXnMZF_KshAnyJufrJvZ9fBYnERSRbwOCRE,3374
55
55
  pyinfra/facts/launchd.py,sha256=D0RSpBQXt4a4zJOxwxIb5RYY34sAx6ZrWj8NxwYqhIc,767
56
56
  pyinfra/facts/lxd.py,sha256=NeIMK9VsDXlRf7q3j17oYbdigL7XjLnhmqv63t6CrzQ,466
@@ -65,7 +65,7 @@ pyinfra/facts/postgres.py,sha256=7cg0Nq8wNIWnKw3B8dJpgjSFZ7q90-_NhwEw2NsSJm8,422
65
65
  pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
66
66
  pyinfra/facts/rpm.py,sha256=SzHNCNUMA-j5uJl4PKRTFpckOvNZ2zpxNeQyOCl8Usk,2225
67
67
  pyinfra/facts/runit.py,sha256=iarF_Tql8bkNeHsKGRANRKNyBWwMsflsTNXj1Wz14i8,2021
68
- pyinfra/facts/selinux.py,sha256=zzh7o0SU8ocMRJzcYMuAv6ss93onfojpa1pAN1azgeU,4433
68
+ pyinfra/facts/selinux.py,sha256=as3AvC6p88er0rYBFTdIWf6k3w0pVjDqDAV9Ur5zY90,4443
69
69
  pyinfra/facts/server.py,sha256=fDXSNNlZghJTGqr9CWRDob-_N-8xxb-KUZlTf5No-M0,20439
70
70
  pyinfra/facts/snap.py,sha256=6br9IMIoq88z_RS0FLXxfodIVjUmyPU9eZBa9zO8H1o,2027
71
71
  pyinfra/facts/systemd.py,sha256=RS6pdcgpIvWMbQeT93O57EKXQzFzR0tF29lCAJQmaAk,4227
@@ -82,17 +82,17 @@ pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGk
82
82
  pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
83
83
  pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
84
84
  pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
85
- pyinfra/operations/apt.py,sha256=YAVZXzCE5zvPQb6FMTcQVVKhdKHUKkV8Ra6fRhD8rYA,13887
85
+ pyinfra/operations/apt.py,sha256=JztxgKeSTienk8pFWCK9vfH-Gg7i1AtUCFbTZDptcuA,13884
86
86
  pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
87
87
  pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
88
88
  pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
89
89
  pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
90
90
  pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
91
91
  pyinfra/operations/docker.py,sha256=RMkrVpS-eeN5zwGnpb3WeeOAoGvFma-A1aPvjE9M1KY,8336
92
- pyinfra/operations/files.py,sha256=iQVpI_7guyQRoc1hkMN2HrA1BbriuPvf5blHkhujA2I,54084
92
+ pyinfra/operations/files.py,sha256=M3Dj9dfBpqNnHxrm0gjDq5kNK_8ERL6iV-TEMme2S0M,54092
93
93
  pyinfra/operations/flatpak.py,sha256=c2OAyuAvt3alVm9D8W6gCfmk5JFydcZD36gO_OhB8Bc,1891
94
94
  pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
95
- pyinfra/operations/git.py,sha256=b26tQF_4hykTy0FtxiuCkqPk9i8JdZaz-RBhH4X96yw,11789
95
+ pyinfra/operations/git.py,sha256=HXBq07YtEQSo-3bkjBvMem5WzeemQ4omHkh6txQxQKs,12487
96
96
  pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
97
97
  pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
98
98
  pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
@@ -109,7 +109,7 @@ pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,
109
109
  pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
110
110
  pyinfra/operations/runit.py,sha256=jRR5kt1OUCLbYktnu7yl3YvSiTW51VvEvOuB0yfd7Ww,5126
111
111
  pyinfra/operations/selinux.py,sha256=imZ4dbY4tl0GpBSkUgV983jbDDihWNs_OQkOBulT7FQ,5948
112
- pyinfra/operations/server.py,sha256=Fi5N53ZirbyybJzonQMIHsgI2CBLGGXTCNrr7IyyW2A,36567
112
+ pyinfra/operations/server.py,sha256=0xDKY9tM7ZvGYxj5zWppmP1fhLUBeGxSsnAxciGifnI,36572
113
113
  pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
114
114
  pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
115
115
  pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
@@ -118,7 +118,7 @@ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE
118
118
  pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
119
119
  pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
120
120
  pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
121
- pyinfra/operations/zfs.py,sha256=vQoeXcwtf-2WXLwBnkM77EhJ_9dQenPXRGcLW07l2_c,5227
121
+ pyinfra/operations/zfs.py,sha256=CYaKf_Yealluo0suk9vuhxt-vEUlLfTVbAO35oalvT8,5271
122
122
  pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
123
123
  pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
124
124
  pyinfra/operations/util/docker.py,sha256=6CvQgeFAXH_lDqKb7RxWpMvlCDwEAXlBaDZoJ8LxrYg,4596
@@ -133,7 +133,7 @@ pyinfra_cli/inventory.py,sha256=vuSL7dU31hxazHmJoUI0c6QjdItG78x8O5ifLUWuMeI,1129
133
133
  pyinfra_cli/log.py,sha256=7WEGtmf3ncF1BtXL2icUjyxeRKy-7XrCcQ2Hg4GWX5Y,2201
134
134
  pyinfra_cli/main.py,sha256=5VTniMcbKuIfjPTzaUklad5fM1BW7CUEARoSV9tPf1U,19954
135
135
  pyinfra_cli/prints.py,sha256=heCF-ugz0F8gTSr--rYVtRqN6jpAun5DUA4cy0F8l5A,9696
136
- pyinfra_cli/util.py,sha256=f3iGIPxlUiQJ5LmUGYbEz0QrySQAKmf9xov9WvHXbrk,6364
136
+ pyinfra_cli/util.py,sha256=s3-Y2AnRaLLULneaDw8YX1scWTFvGBPADPpYf3RtBms,6362
137
137
  pyinfra_cli/virtualenv.py,sha256=6j9W54JkQLN02SrZZIVwszp0GxlaaDEUWFZjBDHIWNA,2466
138
138
  tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
139
  tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
@@ -149,9 +149,9 @@ tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmj
149
149
  tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  tests/test_cli/test_cli.py,sha256=IeWuhkhLzIkRbOEx5-yaW6xV5l4Y8fxaGaDGlMcOyYE,6016
151
151
  tests/test_cli/test_cli_deploy.py,sha256=3tlXpN_ntCvZDeymfQKrZm0kgADkiLUIAGQg5V8-KrU,5079
152
- tests/test_cli/test_cli_exceptions.py,sha256=02sjC6rMptuqchgcdjdsVNQbSQYW6HwGutSy6Q6sMs4,3088
152
+ tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
153
153
  tests/test_cli/test_cli_inventory.py,sha256=xlo-p3HdfVPNqxi7SknEZ2mWrKsdDaK3PoVN-tl95Z0,2394
154
- tests/test_cli/test_cli_util.py,sha256=-Ehnj0cO-EkF-6KLxcPPcFeuAUMTz-fKITrxhuiYhV4,2562
154
+ tests/test_cli/test_cli_util.py,sha256=Fqn_9fnG6xNfw8OMHSZs9hQoW260aSt409S1bUAJs-Q,2500
155
155
  tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
156
156
  tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
157
157
  tests/test_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -164,9 +164,9 @@ tests/test_connectors/test_sshuserclient.py,sha256=2PQNLPhNL6lBACc6tQuXmPoog-9L6
164
164
  tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
165
165
  tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
166
166
  tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
167
- pyinfra-3.1.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
168
- pyinfra-3.1.dist-info/METADATA,sha256=NCTtirew0Vm6uxlBVYrUz6b1aVFoJxHGCV9_MzCS6n0,8039
169
- pyinfra-3.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
170
- pyinfra-3.1.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
171
- pyinfra-3.1.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
172
- pyinfra-3.1.dist-info/RECORD,,
167
+ pyinfra-3.1.1.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
168
+ pyinfra-3.1.1.dist-info/METADATA,sha256=j3fopNCs_edZ2bzLHKAjoCv7_1CFph0D5q56yBJa-sg,8041
169
+ pyinfra-3.1.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
170
+ pyinfra-3.1.1.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
171
+ pyinfra-3.1.1.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
172
+ pyinfra-3.1.1.dist-info/RECORD,,
pyinfra_cli/util.py CHANGED
@@ -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