pyinfra 3.4__py2.py3-none-any.whl → 3.5__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.
@@ -165,7 +165,46 @@ def _remove_network(**kwargs):
165
165
  return "docker network rm {0}".format(kwargs["network"])
166
166
 
167
167
 
168
- def handle_docker(resource, command, **kwargs):
168
+ def _install_plugin(**kwargs):
169
+ command = ["docker plugin install {0} --grant-all-permissions".format(kwargs["plugin"])]
170
+
171
+ plugin_options = kwargs["plugin_options"] if kwargs["plugin_options"] else {}
172
+
173
+ if kwargs["alias"]:
174
+ command.append("--alias {0}".format(kwargs["alias"]))
175
+
176
+ if not kwargs["enabled"]:
177
+ command.append("--disable")
178
+
179
+ for option, value in plugin_options.items():
180
+ command.append("{0}={1}".format(option, value))
181
+
182
+ return " ".join(command)
183
+
184
+
185
+ def _remove_plugin(**kwargs):
186
+ return "docker plugin rm -f {0}".format(kwargs["plugin"])
187
+
188
+
189
+ def _enable_plugin(**kwargs):
190
+ return "docker plugin enable {0}".format(kwargs["plugin"])
191
+
192
+
193
+ def _disable_plugin(**kwargs):
194
+ return "docker plugin disable {0}".format(kwargs["plugin"])
195
+
196
+
197
+ def _set_plugin_options(**kwargs):
198
+ command = ["docker plugin set {0}".format(kwargs["plugin"])]
199
+ existent_options = kwargs.get("existing_options", {})
200
+ required_options = kwargs.get("required_options", {})
201
+ options_to_set = existent_options | required_options
202
+ for option, value in options_to_set.items():
203
+ command.append("{0}={1}".format(option, value))
204
+ return " ".join(command)
205
+
206
+
207
+ def handle_docker(resource: str, command: str, **kwargs):
169
208
  container_commands = {
170
209
  "create": _create_container,
171
210
  "remove": _remove_container,
@@ -192,12 +231,21 @@ def handle_docker(resource, command, **kwargs):
192
231
  "prune": _prune_command,
193
232
  }
194
233
 
234
+ plugin_commands = {
235
+ "install": _install_plugin,
236
+ "remove": _remove_plugin,
237
+ "enable": _enable_plugin,
238
+ "disable": _disable_plugin,
239
+ "set": _set_plugin_options,
240
+ }
241
+
195
242
  docker_commands = {
196
243
  "container": container_commands,
197
244
  "image": image_commands,
198
245
  "volume": volume_commands,
199
246
  "network": network_commands,
200
247
  "system": system_commands,
248
+ "plugin": plugin_commands,
201
249
  }
202
250
 
203
251
  return docker_commands[resource][command](**kwargs)
@@ -1,12 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import difflib
3
4
  import re
4
- from datetime import datetime
5
- from typing import Callable
5
+ from datetime import datetime, timezone
6
+ from enum import Enum
7
+ from typing import Callable, Generator
8
+
9
+ import click
6
10
 
7
11
  from pyinfra.api import QuoteString, StringCommand
8
12
 
9
13
 
14
+ class MetadataTimeField(Enum):
15
+ ATIME = "atime"
16
+ MTIME = "mtime"
17
+
18
+
10
19
  def unix_path_join(*parts) -> str:
11
20
  part_list = list(parts)
12
21
  part_list[0:-1] = [part.rstrip("/") for part in part_list[0:-1]]
@@ -156,6 +165,34 @@ def chown(
156
165
  return StringCommand(" ".join(args), user_group, QuoteString(target))
157
166
 
158
167
 
168
+ # like the touch command, but only supports setting one field at a time, and expects any
169
+ # reference times to have been read from the reference file metadata and turned into
170
+ # aware datetimes
171
+ def touch(
172
+ target: str,
173
+ timefield: MetadataTimeField,
174
+ timesrc: datetime,
175
+ dereference=True,
176
+ ) -> StringCommand:
177
+ args = ["touch"]
178
+
179
+ if timefield is MetadataTimeField.ATIME:
180
+ args.append("-a")
181
+ else:
182
+ args.append("-m")
183
+
184
+ if not dereference:
185
+ args.append("-h")
186
+
187
+ # don't reinvent the wheel; use isoformat()
188
+ timestr = timesrc.astimezone(timezone.utc).isoformat()
189
+ # but replace the ISO format TZ offset with "Z" for BSD
190
+ timestr = timestr.replace("+00:00", "Z")
191
+ args.extend(["-d", timestr])
192
+
193
+ return StringCommand(" ".join(args), QuoteString(target))
194
+
195
+
159
196
  def adjust_regex(line: str, escape_regex_characters: bool) -> str:
160
197
  """
161
198
  Ensure the regex starts with '^' and ends with '$' and escape regex characters if requested
@@ -173,3 +210,34 @@ def adjust_regex(line: str, escape_regex_characters: bool) -> str:
173
210
  match_line = "{0}.*$".format(match_line)
174
211
 
175
212
  return match_line
213
+
214
+
215
+ def generate_color_diff(
216
+ current_lines: list[str], desired_lines: list[str]
217
+ ) -> Generator[str, None, None]:
218
+ def _format_range_unified(start: int, stop: int) -> str:
219
+ beginning = start + 1 # lines start numbering with one
220
+ length = stop - start
221
+ if length == 1:
222
+ return "{}".format(beginning)
223
+ if not length:
224
+ beginning -= 1 # empty ranges begin at line just before the range
225
+ return "{},{}".format(beginning, length)
226
+
227
+ for group in difflib.SequenceMatcher(None, current_lines, desired_lines).get_grouped_opcodes(2):
228
+ first, last = group[0], group[-1]
229
+ file1_range = _format_range_unified(first[1], last[2])
230
+ file2_range = _format_range_unified(first[3], last[4])
231
+ yield "@@ -{} +{} @@".format(file1_range, file2_range)
232
+
233
+ for tag, i1, i2, j1, j2 in group:
234
+ if tag == "equal":
235
+ for line in current_lines[i1:i2]:
236
+ yield " " + line.rstrip()
237
+ continue
238
+ if tag in {"replace", "delete"}:
239
+ for line in current_lines[i1:i2]:
240
+ yield click.style("- " + line.rstrip(), "red")
241
+ if tag in {"replace", "insert"}:
242
+ for line in desired_lines[j1:j2]:
243
+ yield click.style("+ " + line.rstrip(), "green")
@@ -3,19 +3,83 @@ from __future__ import annotations
3
3
  import shlex
4
4
  from collections import defaultdict
5
5
  from io import StringIO
6
- from typing import Callable
6
+ from typing import Callable, NamedTuple, cast
7
7
  from urllib.parse import urlparse
8
8
 
9
- from pyinfra.api import Host, State
9
+ from packaging.requirements import InvalidRequirement, Requirement
10
+
11
+ from pyinfra import logger
12
+ from pyinfra.api import Host, OperationValueError, State
10
13
  from pyinfra.facts.files import File
11
14
  from pyinfra.facts.rpm import RpmPackage
12
15
  from pyinfra.operations import files
13
16
 
14
17
 
15
- def _package_name(package: list[str] | str) -> str:
16
- if isinstance(package, list):
17
- return package[0]
18
- return package
18
+ class PkgInfo(NamedTuple):
19
+ name: str
20
+ version: str
21
+ operator: str
22
+ url: str
23
+ """
24
+ The key packaging information needed: version, operator and url are optional.
25
+ """
26
+
27
+ @property
28
+ def lkup_name(self) -> str | list[str]:
29
+ return self.name if self.version == "" else [self.name, self.version]
30
+
31
+ @property
32
+ def has_version(self) -> bool:
33
+ return self.version != ""
34
+
35
+ @property
36
+ def inst_vers(self) -> str:
37
+ return (
38
+ self.url
39
+ if self.url != ""
40
+ else (
41
+ self.operator.join([self.name, self.version]) if self.version != "" else self.name
42
+ )
43
+ )
44
+
45
+ @classmethod
46
+ def from_possible_pair(cls, s: str, join: str | None) -> PkgInfo:
47
+ if join is not None:
48
+ pieces = s.rsplit(join, 1)
49
+ return cls(pieces[0], pieces[1] if len(pieces) > 1 else "", join, "")
50
+
51
+ return cls(s, "", "", "")
52
+
53
+ @classmethod
54
+ def from_pep508(cls, s: str) -> PkgInfo | None:
55
+ """
56
+ Separate out the useful parts (name, url, operator, version) of a PEP-508 dependency.
57
+ Note: only one specifier is allowed.
58
+ PEP-0426 states that Python packages should be compared using lowercase; thus
59
+ the name is lower-cased
60
+ For backwards compatibility, invalid requirements are assumed to be package names with a
61
+ warning that this will change in the next major release
62
+ """
63
+ pep_508 = "PEP 508 non-compliant "
64
+ treatment = "requirement treated as package name"
65
+ will_change = "4.x will make this an error" # pip and pipx already throw away None's
66
+ try:
67
+ reqt = Requirement(s)
68
+ except InvalidRequirement as e:
69
+ logger.warning(f"{pep_508} :{e}\n{will_change}")
70
+ return cls(s, "", "", "")
71
+ else:
72
+ if (len(reqt.specifier) > 0) and (len(reqt.specifier) > 1):
73
+ logger.warning(f"{pep_508}/unsupported specifier ({s}) {treatment}\n{will_change}")
74
+ return cls(s, "", "", "")
75
+ else:
76
+ spec = next(iter(reqt.specifier), None)
77
+ return cls(
78
+ reqt.name.lower(),
79
+ spec.version if spec is not None else "",
80
+ spec.operator if spec is not None else "",
81
+ reqt.url or "",
82
+ )
19
83
 
20
84
 
21
85
  def _has_package(
@@ -57,12 +121,12 @@ def _has_package(
57
121
 
58
122
  def ensure_packages(
59
123
  host: Host,
60
- packages_to_ensure: str | list[str] | None,
124
+ packages_to_ensure: str | list[str] | list[PkgInfo] | None,
61
125
  current_packages: dict[str, set[str]],
62
126
  present: bool,
63
127
  install_command: str,
64
128
  uninstall_command: str,
65
- latest=False,
129
+ latest: bool = False,
66
130
  upgrade_command: str | None = None,
67
131
  version_join: str | None = None,
68
132
  expand_package_fact: Callable[[str], list[str | list[str]]] | None = None,
@@ -70,22 +134,22 @@ def ensure_packages(
70
134
  """
71
135
  Handles this common scenario:
72
136
 
73
- + We have a list of packages(/versions) to ensure
137
+ + We have a list of packages(/versions/urls) to ensure
74
138
  + We have a map of existing package -> versions
75
139
  + We have the common command bits (install, uninstall, version "joiner")
76
140
  + Outputs commands to ensure our desired packages/versions
77
141
  + Optionally upgrades packages w/o specified version when present
78
142
 
79
143
  Args:
80
- packages_to_ensure (list): list of packages or package/versions
81
- current_packages (fact): fact returning dict of package names -> version
144
+ packages_to_ensure (list): list of packages or package/versions or PkgInfo's
145
+ current_packages (dict): dict of package names -> version
82
146
  present (bool): whether packages should exist or not
83
147
  install_command (str): command to prefix to list of packages to install
84
148
  uninstall_command (str): as above for uninstalling packages
85
149
  latest (bool): whether to upgrade installed packages when present
86
150
  upgrade_command (str): as above for upgrading
87
151
  version_join (str): the package manager specific "joiner", ie ``=`` for \
88
- ``<apt_pkg>=<version>``
152
+ ``<apt_pkg>=<version>``. Not allowed if (pkg, ver, url) tuples are provided.
89
153
  expand_package_fact: fact returning packages providing a capability \
90
154
  (ie ``yum whatprovides``)
91
155
  """
@@ -95,12 +159,15 @@ def ensure_packages(
95
159
  if isinstance(packages_to_ensure, str):
96
160
  packages_to_ensure = [packages_to_ensure]
97
161
 
98
- packages: list[str | list[str]] = packages_to_ensure # type: ignore[assignment]
99
-
100
- if version_join:
162
+ packages: list[PkgInfo] = []
163
+ if isinstance(packages_to_ensure[0], PkgInfo):
164
+ packages = cast("list[PkgInfo]", packages_to_ensure)
165
+ if version_join is not None:
166
+ raise OperationValueError("cannot specify version_join and provide list[PkgInfo]")
167
+ else:
101
168
  packages = [
102
- package[0] if len(package) == 1 else package
103
- for package in [package.rsplit(version_join, 1) for package in packages] # type: ignore[union-attr] # noqa
169
+ PkgInfo.from_possible_pair(package, version_join)
170
+ for package in cast("list[str]", packages_to_ensure)
104
171
  ]
105
172
 
106
173
  diff_packages = []
@@ -111,65 +178,41 @@ def ensure_packages(
111
178
  if present is True:
112
179
  for package in packages:
113
180
  has_package, expanded_packages = _has_package(
114
- package,
115
- current_packages,
116
- expand_package_fact,
181
+ package.lkup_name, current_packages, expand_package_fact
117
182
  )
118
183
 
119
184
  if not has_package:
120
- diff_packages.append(package)
121
- diff_expanded_packages[_package_name(package)] = expanded_packages
185
+ diff_packages.append(package.inst_vers)
186
+ diff_expanded_packages[package.name] = expanded_packages
122
187
  else:
123
188
  # Present packages w/o version specified - for upgrade if latest
124
- if isinstance(package, str):
125
- upgrade_packages.append(package)
189
+ if not package.has_version: # don't try to upgrade if a specific version requested
190
+ upgrade_packages.append(package.inst_vers)
126
191
 
127
192
  if not latest:
128
- pkg_name = _package_name(package)
129
- if pkg_name in current_packages:
130
- host.noop(
131
- "package {0} is installed ({1})".format(
132
- package,
133
- ", ".join(current_packages[pkg_name]),
134
- ),
135
- )
193
+ if (pkg := package.name) in current_packages:
194
+ host.noop(f"package {pkg} is installed ({','.join(current_packages[pkg])})")
136
195
  else:
137
- host.noop("package {0} is installed".format(package))
196
+ host.noop(f"package {package.name} is installed")
138
197
 
139
198
  if present is False:
140
199
  for package in packages:
141
- # String version, just check if existing
142
200
  has_package, expanded_packages = _has_package(
143
- package,
144
- current_packages,
145
- expand_package_fact,
146
- match_any=True,
201
+ package.lkup_name, current_packages, expand_package_fact, match_any=True
147
202
  )
148
203
 
149
204
  if has_package:
150
- diff_packages.append(package)
151
- diff_expanded_packages[_package_name(package)] = expanded_packages
205
+ diff_packages.append(package.inst_vers)
206
+ diff_expanded_packages[package.name] = expanded_packages
152
207
  else:
153
- host.noop("package {0} is not installed".format(package))
208
+ host.noop(f"package {package.name} is not installed")
154
209
 
155
210
  if diff_packages:
156
211
  command = install_command if present else uninstall_command
157
-
158
- joined_packages = [
159
- version_join.join(package) if isinstance(package, list) else package # type: ignore[union-attr] # noqa
160
- for package in diff_packages
161
- ]
162
-
163
- yield "{0} {1}".format(
164
- command,
165
- " ".join([shlex.quote(pkg) for pkg in joined_packages]),
166
- )
212
+ yield f"{command} {' '.join([shlex.quote(pkg) for pkg in diff_packages])}"
167
213
 
168
214
  if latest and upgrade_command and upgrade_packages:
169
- yield "{0} {1}".format(
170
- upgrade_command,
171
- " ".join([shlex.quote(pkg) for pkg in upgrade_packages]),
172
- )
215
+ yield f"{upgrade_command} {' '.join([shlex.quote(pkg) for pkg in upgrade_packages])}"
173
216
 
174
217
 
175
218
  def ensure_rpm(state: State, host: Host, source: str, present: bool, package_manager_command: str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.4
3
+ Version: 3.5
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -52,7 +52,7 @@ Requires-Dist: flake8-isort ==6.1.2 ; extra == 'dev'
52
52
  Requires-Dist: pyyaml ==6.0.2 ; extra == 'dev'
53
53
  Requires-Dist: mypy ; extra == 'dev'
54
54
  Requires-Dist: types-cryptography ; extra == 'dev'
55
- Requires-Dist: types-paramiko ; extra == 'dev'
55
+ Requires-Dist: types-paramiko <4 ; extra == 'dev'
56
56
  Requires-Dist: types-python-dateutil ; extra == 'dev'
57
57
  Requires-Dist: types-PyYAML ; extra == 'dev'
58
58
  Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.17 ; extra == 'dev'
@@ -83,7 +83,7 @@ Requires-Dist: flake8-isort ==6.1.2 ; extra == 'test'
83
83
  Requires-Dist: pyyaml ==6.0.2 ; extra == 'test'
84
84
  Requires-Dist: mypy ; extra == 'test'
85
85
  Requires-Dist: types-cryptography ; extra == 'test'
86
- Requires-Dist: types-paramiko ; extra == 'test'
86
+ Requires-Dist: types-paramiko <4 ; extra == 'test'
87
87
  Requires-Dist: types-python-dateutil ; extra == 'test'
88
88
  Requires-Dist: types-PyYAML ; extra == 'test'
89
89
 
@@ -6,20 +6,20 @@ 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=-R1qdnwLZ79gJL8ggHTxQjz2vJgJDZN_ds42RjdcYBA,10014
9
+ pyinfra/api/arguments.py,sha256=pxfizvECzEahbIyPfmVnk9FsQFnOnDIFPhBZvFLEhFg,12231
10
10
  pyinfra/api/arguments_typed.py,sha256=IZuhpmDfW9CP6ASS5Ie-3Wcnxl6bDNR3egU4Mfhbsb4,2303
11
11
  pyinfra/api/command.py,sha256=NwF2syxV3zxCbBdHzvJ6ve5G-xwdNTjPHFPwguKVcYs,7741
12
- pyinfra/api/config.py,sha256=LhmMKOoAaawSsrSnl55BBj63oT-U-Wn8nwgbe43kzd0,9113
13
- pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
12
+ pyinfra/api/config.py,sha256=gVDV-aGh6LYOnHtBaivICrd3RBfjFRWy3-K9sG__eP8,9321
13
+ pyinfra/api/connect.py,sha256=jkx07iUL29u9pHHKH4WcNtvxwOA4DIbF7ixguFyuFjo,1984
14
14
  pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
15
15
  pyinfra/api/deploy.py,sha256=Upd92oThQN0zhKbKW8vyPvBuoYiEGStuiEy7kNhZ00Y,3167
16
16
  pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
17
17
  pyinfra/api/facts.py,sha256=Hh9YCrqOppHdeUegOzmg6qbik5X6EyHPzhg8O_oF_Sg,9146
18
18
  pyinfra/api/host.py,sha256=4a1bFR8vX8mUuXRZttXlzp2_ARzrg-xsH7n8uOxaaqQ,14098
19
19
  pyinfra/api/inventory.py,sha256=i_LBI-Gn5FF-9SVDBH6xefTtvFzjuz12QQiFPGK2TrQ,7864
20
- pyinfra/api/operation.py,sha256=_7R2piJDpkcUnC6rxbZjgkeGcWuwTHS5Aruboc8S-wI,15207
21
- pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
22
- pyinfra/api/state.py,sha256=IqdWT1Uwf_Rd5piqYLrf7y8HsXPs1hY6wCmxoRGqPTE,12630
20
+ pyinfra/api/operation.py,sha256=37lV_87G7KGCtPGsFSwHW-OQRGzBy-IsQpslRL0s9Hg,16870
21
+ pyinfra/api/operations.py,sha256=MedR-5W4BF23p_FMI7o62K72pgE7G1z2scKeC-b3JF0,13677
22
+ pyinfra/api/state.py,sha256=cj-JvxOljeDshWvRpq8AMQxdGaUaht8KyuyR3mEsI-Y,12859
23
23
  pyinfra/api/util.py,sha256=62l3eLUhbcHm5N-Y58i7g8-NtAxuXakeOymDBptTjQ4,12838
24
24
  pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  pyinfra/connectors/base.py,sha256=0-BKInwTpjbnTakJhMTn_8LUOl81vUmC-q-HzVrwhkw,4703
@@ -27,11 +27,13 @@ pyinfra/connectors/chroot.py,sha256=sQIM1nOLzyIvv7FsqgAZsZ71jJtWLAQB_uaeB4m4GqE,
27
27
  pyinfra/connectors/docker.py,sha256=At68g9Fx62Wm9dum0GLmQRkk0sr5NCYHWM6Zlfxx1Gc,11960
28
28
  pyinfra/connectors/dockerssh.py,sha256=NGG6hSZ3z66RRKt6T0hnG2jiXFLR4P5uudi1COcGnY0,9021
29
29
  pyinfra/connectors/local.py,sha256=DJMmZiPgeYvZyW0ANvWjCxF3elmDlmolkY0ktTW3rcg,6990
30
- pyinfra/connectors/ssh.py,sha256=HmYHvPXVZKFsqWTtHzVrplyouk1ziv99hJN04ZUGeaY,21342
30
+ pyinfra/connectors/ssh.py,sha256=Wxw23KKKLKU_DVq1lLdsHHwDzXR4_He99-ikg9CCxps,22576
31
31
  pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
32
  pyinfra/connectors/terraform.py,sha256=j-a2yStBLdw1QLZRVnl_9TanDtdyujyCxBO2Oa00rPM,4289
33
- pyinfra/connectors/util.py,sha256=Up7kToERfcyjoayY9K6-Xdwjg4-SO2THAy7jzzcznJ8,11651
33
+ pyinfra/connectors/util.py,sha256=-Ei6FMIzbVHD7G1R834Tg7RLoNzo-62wUYIkj043MRQ,11780
34
34
  pyinfra/connectors/vagrant.py,sha256=0TT73ks64I4Yl-JSZjMBbpWA3VYBkqqLB-fUS8pS8GY,4813
35
+ pyinfra/connectors/scp/__init__.py,sha256=jnO-_8GfkKWhsFcDjAxjOkuUT2RbS22b8P_xPrX889U,44
36
+ pyinfra/connectors/scp/client.py,sha256=l_fPsbgz-7U6Y50ssuKKPFxD_cFoIPtaVXMCYDotbDI,6399
35
37
  pyinfra/connectors/sshuserclient/__init__.py,sha256=Qc4RO2wknSWIiNTwOeQ0y2TeiuKHmyWDW2Dz4MOo9CE,44
36
38
  pyinfra/connectors/sshuserclient/client.py,sha256=Ei2_mzCMNJopbpvpeLsdSiNb98rxEEy7uCOmpJbfd2o,10506
37
39
  pyinfra/connectors/sshuserclient/config.py,sha256=FZkPrUYXkURZcFUHBGWw9lLC9uiH3DJ0rBYXJePchxw,2774
@@ -44,10 +46,10 @@ pyinfra/facts/cargo.py,sha256=qgOClhwZm4COcncDzOZccCzs67nPBi_x6VGiF2UA0sA,687
44
46
  pyinfra/facts/choco.py,sha256=mpLleSqNqiaGRgyrhgceno2iPB1_1yjn8UJ90pvOZCs,886
45
47
  pyinfra/facts/crontab.py,sha256=yCpBzBqgXt2BjAGCIttuQ2xKKtuhzg0VQ9WCDV46eV8,5754
46
48
  pyinfra/facts/deb.py,sha256=1dR1puwY5wyyhhYYwaEBLjKU9sIyaNBNBlamVZ2KQg0,2074
47
- pyinfra/facts/dnf.py,sha256=IFOJWeXMLYxSZEgleOCMZaBIZOzJzklwCmemZIUwDmk,1059
48
- pyinfra/facts/docker.py,sha256=78jYZNUT6_9YFpECiULiBgSyt2stpqrnsrtL20zElx8,2772
49
+ pyinfra/facts/dnf.py,sha256=wXatfZWVrrdLY7LM-vHKMg8Md1FiwkqHxmgRYbQqw90,1208
50
+ pyinfra/facts/docker.py,sha256=fqIqMR6HwSYpTUAjhCX8Hk57pcyL6ShIl98H32Ly6HM,3233
49
51
  pyinfra/facts/efibootmgr.py,sha256=JPJSokE_RV9JstEPJRECnqSU-B0JCxmrocY8zBOva7M,3555
50
- pyinfra/facts/files.py,sha256=bnu3JSoWRdOR1Uxv9nBn1DbanBeF3kWet_rc-3Aj63s,15363
52
+ pyinfra/facts/files.py,sha256=bDj3QOk149J9PjFPpyH4uynLhUl1nd8VQFwxclsgIuE,19459
51
53
  pyinfra/facts/flatpak.py,sha256=ovi3duwTqqwvt5GoDRN7R-PpkvR6sQ1SmgEADcSnkUE,1646
52
54
  pyinfra/facts/freebsd.py,sha256=z7ZJRK8NV5HL-BfAdQs6pQKLEdrfdv7dVUmmOtVKUbA,2248
53
55
  pyinfra/facts/gem.py,sha256=aX2-vcEqkxUIP0UJ_SVp9bf4B944oyDjsuujjs5q_9w,654
@@ -72,19 +74,19 @@ pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,2
72
74
  pyinfra/facts/rpm.py,sha256=ikuKYiUmjgvPA84qfE-gbq4Iv4AB5cvor1uKU6uHbXQ,2391
73
75
  pyinfra/facts/runit.py,sha256=qok1FTSshiNrN603vjYTKOeM-NIlxwLbwOp-vPbPySo,2131
74
76
  pyinfra/facts/selinux.py,sha256=8OylQ3H-huktRS34g5FYwivlp1wb4mKP0EFVf1LtMEE,4679
75
- pyinfra/facts/server.py,sha256=_Th60Ya-P6Q89i7JDklzCn_jJ2Rb28i45riUbwTEjUM,21958
77
+ pyinfra/facts/server.py,sha256=W_ICGPvlCcaG-Majm5tTM2fZDho6wYCR4xUt19iOX1A,23444
76
78
  pyinfra/facts/snap.py,sha256=2-c3z1qpOG7prmKJssLpOXmKo_wwdfROryro6gif2vo,2137
77
79
  pyinfra/facts/systemd.py,sha256=meHXURtnoxeJbmPzWFTwvhjQBZ2NlQCY8Tj-oHTG_dI,4320
78
80
  pyinfra/facts/sysvinit.py,sha256=q1OpHATFJxjLwatcnYRfpTR7_K2c29b4ppmZu-wgC-4,1589
79
81
  pyinfra/facts/upstart.py,sha256=GcreN0mIM6_qRgqzFaA7PnX45RtbBpvVC00J6bKujyA,717
80
82
  pyinfra/facts/vzctl.py,sha256=lUacmyakn2sJ2tD2FDAh1eeX3sxEVq7aRRwWM4QTguQ,760
81
83
  pyinfra/facts/xbps.py,sha256=pNpgeITdHoJWhnJ_XFjySJ7H35d9h_v2F7GKqIrxgt0,663
82
- pyinfra/facts/yum.py,sha256=OE1CJrX0AFOHuOdoPMfZuqUQ8hMLgJPtZGCzC9HH4Ho,1020
84
+ pyinfra/facts/yum.py,sha256=KM0ogmT2JPPuSZKV7HaUFxIA1IXhcXRmykRk9wloKag,1169
83
85
  pyinfra/facts/zfs.py,sha256=4cWfTu2_V3Rkku8LfWzwruP_Tu4gJV2ZOrtW3otbP2w,1805
84
- pyinfra/facts/zypper.py,sha256=RRSGrWAfgyiFVQAesjKpYf6i3M2d2Fagdb69BSF0iEM,955
86
+ pyinfra/facts/zypper.py,sha256=OQ8VXA-aRpsleiXVaRotjOewSOknux_VQ1Xv3qlfy7k,958
85
87
  pyinfra/facts/util/__init__.py,sha256=FNqUZjHPzJplb6ctHdZCVvmxAeVDQfFYmghC6B5edWQ,573
86
88
  pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
87
- pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGkrk,1181
89
+ pyinfra/facts/util/packaging.py,sha256=Bo7QxYO0Eyj4_i8G27aOWUD_Rfw741RhkEBm3dF5OMI,1229
88
90
  pyinfra/facts/util/units.py,sha256=SNHCisxGwZedCOqO9tfOWJpZ5Stc0Wcg9mZcXoKBY0A,714
89
91
  pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
90
92
  pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
@@ -94,23 +96,23 @@ pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,51
94
96
  pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
95
97
  pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
96
98
  pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
97
- pyinfra/operations/crontab.py,sha256=tdgOSKTO71Nxto6rcBbuDZQRfzBYrR-ucp73M0hadS8,6551
99
+ pyinfra/operations/crontab.py,sha256=4jxNsgmJED7w9w2Rq6Irsa4wMXxH_yPDIjkFlcpXj7E,6552
98
100
  pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
99
- pyinfra/operations/docker.py,sha256=39MFhUs1edbzbG8ejR-r3-xczeV8op7kFerk6qQieoU,8520
100
- pyinfra/operations/files.py,sha256=ZnHsZ-qcvVO_boHnpAT-yvzL_F8j4NTiFY5KkeFCzIE,57814
101
+ pyinfra/operations/docker.py,sha256=a-RlZ2_jzIXGEITDax9r_fKXBuGzpk-X_dP7pOto3lM,12272
102
+ pyinfra/operations/files.py,sha256=XfOG6rAqGQOJ9Ow1F8lrL2zTPFCbQjv0V9zqPwXYbVw,64620
101
103
  pyinfra/operations/flatpak.py,sha256=QEJpzSXLyMQFk1XPAPHAfJVWcYWHnKA-tQr2hX6sB9o,2319
102
104
  pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
103
- pyinfra/operations/git.py,sha256=ETENOkGfs9Io27mguK8tlRn0Zbw0eTvtFvRaE2H6Bsk,13236
105
+ pyinfra/operations/git.py,sha256=BqYQbG1VRw_XPtagbZHXitzss-U_LNGXRTwHE7L8P6M,13244
104
106
  pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
105
107
  pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
106
108
  pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
107
109
  pyinfra/operations/mysql.py,sha256=ctm2Z6MaB0mOArCNU4TsJzaXiKXQaa_ahmsC5Vvyi10,19857
108
110
  pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
109
- pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
111
+ pyinfra/operations/openrc.py,sha256=AThQQO7u_pO0M-rQbxkX7EWDuR569smkoPVaQoRoFeE,1834
110
112
  pyinfra/operations/opkg.py,sha256=xBdmU07MWr-YBKX0EV0GuVrwMy6MOOf7wzYvD42sF_I,2607
111
113
  pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
112
- pyinfra/operations/pip.py,sha256=MCmb9FPcyvg6M7lTlRtx2qpXHtyf-SwBVtHqcAkqVzQ,5925
113
- pyinfra/operations/pipx.py,sha256=PFVXriRIk5gnJXWcFoghsCIVfTy7RvQyvDggjXHALQc,2188
114
+ pyinfra/operations/pip.py,sha256=Gh1vmrkCNktYXO8bh0IMEqjqhV07J8FYJcLHETZhjcQ,5995
115
+ pyinfra/operations/pipx.py,sha256=05AUm3nkkCfoxl0o4V5o9P_qb__0Yfv495dTBq7zkE4,2800
114
116
  pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
115
117
  pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
116
118
  pyinfra/operations/postgres.py,sha256=hD65hRb8s3h6zlG-9WwT4JsNEXuxJUCY8ZRX61qlvlI,13367
@@ -136,9 +138,9 @@ pyinfra/operations/freebsd/pkg.py,sha256=3AyfI0-_9F4ho47KqZsOMQocwNtTF2q9g0i6Tff
136
138
  pyinfra/operations/freebsd/service.py,sha256=1f7nTHELnhs3HBSrMFsmopVgYFMIwB8Se88yneRQ8Rw,3198
137
139
  pyinfra/operations/freebsd/sysrc.py,sha256=eg7u_JsCge_uKq3Ysc_mohUc6qgJrOZStp_B_l2Hav4,2330
138
140
  pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
139
- pyinfra/operations/util/docker.py,sha256=LetDzYhB4jgNPT5ccYf6Wk1--XQuwN_pcNBe8tcJJIU,5485
140
- pyinfra/operations/util/files.py,sha256=BH2My7PeB9OWoW6UYhRSEwCm9_y6G6qa6BTdSxbf0YA,4817
141
- pyinfra/operations/util/packaging.py,sha256=xFtOlEX46ms7g3gDvOOInRVR1RVfgsmhLzFzsJAL_eU,9381
141
+ pyinfra/operations/util/docker.py,sha256=F7YmWIRSDyFaosf9q81nnwHhhIG7ktXOkM4VVOiUXTs,6931
142
+ pyinfra/operations/util/files.py,sha256=uW9CKQMwfBrG0iEArI5ubnkBNWJEYRJAyq26A7Gcafc,7118
143
+ pyinfra/operations/util/packaging.py,sha256=EHbjWiG6x5ekBtS09bQirqGrQNjJiqv5H7lYkNNj2bA,11410
142
144
  pyinfra/operations/util/service.py,sha256=kJd1zj4-sAaGIp5Ts7yAJznogWaGr8oQTztwenLAr7Y,1309
143
145
  pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
144
146
  pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
@@ -146,8 +148,8 @@ pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
146
148
  pyinfra_cli/exceptions.py,sha256=RRaOprL7SmVv--FLy4x7fxeTitx9wYI0Y3_h01LfhJA,4901
147
149
  pyinfra_cli/inventory.py,sha256=JYSixJZKY8GNWlOxh-nGDsAknCdaAktlWAmdg13kvNk,11771
148
150
  pyinfra_cli/log.py,sha256=mD96MH2owQQ5AsYRw7osCKENdp-E3Wum5IDr6qhSIa4,2268
149
- pyinfra_cli/main.py,sha256=_cW7PNVxMjwEl8IA8GSU15XrMCvFapFurnqDVCHgt2E,20111
150
- pyinfra_cli/prints.py,sha256=eaLRQAdiVGmPR2GsHF_fCY706aSt5JyXxz42z6qLAFM,10299
151
+ pyinfra_cli/main.py,sha256=XUAwv-SrmZYiyW73DrC2ZmWYnVNp-aIp05WIO4xbSKo,20939
152
+ pyinfra_cli/prints.py,sha256=1h6vgKVRKUxcGz_HdyEEDUvkp-lgiiVGwx3hc9rw24A,10434
151
153
  pyinfra_cli/util.py,sha256=9ehdJQ8pDNBLHXoFst9p696VDT-b_qo8UFMJrqdE1rE,6424
152
154
  pyinfra_cli/virtualenv.py,sha256=wRNxOPcUkbD_Pzuj-Lnrz1KxGmsLlb2ObmCTFrdD-S8,2474
153
155
  tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -159,10 +161,10 @@ tests/test_api/test_api_deploys.py,sha256=h_zbI6CK4K8SdzEr3LEAMPxOf9hnQBdi_suqiN
159
161
  tests/test_api/test_api_facts.py,sha256=_Z3g5mN3PJdqVGHYxwNUBqPoe6FZBiJEjBEJ0zS9EtY,11047
160
162
  tests/test_api/test_api_host.py,sha256=U_VW2vTl35vR8EdyIGMKr4y0ydsDLbvHSjZDa99CyNE,1119
161
163
  tests/test_api/test_api_inventory.py,sha256=rqXd3e_Wwc-SxCzxgR5eLd7ZOdrF8CcHbcTZndLy5gE,2744
162
- tests/test_api/test_api_operations.py,sha256=5x4UJspW12_z3HNe5NcivnPXeGfLdmgC13V5iUTUxyw,20385
164
+ tests/test_api/test_api_operations.py,sha256=GLfTUjMSZAq9uvQdAjIf_0BJhSoLaypBJbrF0R_eAPw,32627
163
165
  tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
164
166
  tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
- tests/test_cli/test_cli.py,sha256=I9QMrp0xI_5p2N3ZyeNBBcPTUNkPVDdQvlWM860YIwM,6016
167
+ tests/test_cli/test_cli.py,sha256=Srg7j3sdYlbQshO2BgwDEakKCjG2XZUu2lV-SgGWcCI,6099
166
168
  tests/test_cli/test_cli_deploy.py,sha256=vZC7twj8sPCy05loO50P-D_Xf73r6XN4m7yVj7TIFmo,5243
167
169
  tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
168
170
  tests/test_cli/test_cli_inventory.py,sha256=FD4Hc4MzOfnrZTugVeNPOBf7bvE1degpDWQf5s6LZAg,4036
@@ -179,9 +181,9 @@ tests/test_connectors/test_sshuserclient.py,sha256=_anSd1cVQGIQkn08RdRbWjnSEkSS5
179
181
  tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
180
182
  tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
181
183
  tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
182
- pyinfra-3.4.dist-info/LICENSE.md,sha256=BzCnRYLJv0yb-FJuEd_XOrrQSOEQKzIVo0yHT8taNnM,1076
183
- pyinfra-3.4.dist-info/METADATA,sha256=OgZy8dxZfcjuGsrJM8J_aZVOHOX0O8M4GxycFOhUyxo,8135
184
- pyinfra-3.4.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
185
- pyinfra-3.4.dist-info/entry_points.txt,sha256=MT4cxTreOa_MzKgyPXk2BkeQ-KKcF1P8PJQe4vLQDaw,522
186
- pyinfra-3.4.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
187
- pyinfra-3.4.dist-info/RECORD,,
184
+ pyinfra-3.5.dist-info/LICENSE.md,sha256=BzCnRYLJv0yb-FJuEd_XOrrQSOEQKzIVo0yHT8taNnM,1076
185
+ pyinfra-3.5.dist-info/METADATA,sha256=pHq25twR3t_qYHepFbZFFNO4zF7PYYG4jymQbvkgEts,8141
186
+ pyinfra-3.5.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
187
+ pyinfra-3.5.dist-info/entry_points.txt,sha256=MT4cxTreOa_MzKgyPXk2BkeQ-KKcF1P8PJQe4vLQDaw,522
188
+ pyinfra-3.5.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
189
+ pyinfra-3.5.dist-info/RECORD,,