pyinfra 2.9.2__py2.py3-none-any.whl → 3.0b1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +261 -255
  3. pyinfra/api/arguments_typed.py +77 -0
  4. pyinfra/api/command.py +66 -53
  5. pyinfra/api/config.py +27 -22
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +2 -24
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +77 -113
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +17 -25
  13. pyinfra/api/operation.py +232 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +55 -70
  17. pyinfra/connectors/base.py +150 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +227 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +195 -207
  22. pyinfra/connectors/ssh.py +528 -615
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +212 -137
  27. pyinfra/connectors/vagrant.py +55 -48
  28. pyinfra/context.py +3 -2
  29. pyinfra/facts/docker.py +1 -0
  30. pyinfra/facts/files.py +45 -32
  31. pyinfra/facts/git.py +3 -1
  32. pyinfra/facts/gpg.py +1 -1
  33. pyinfra/facts/hardware.py +4 -2
  34. pyinfra/facts/iptables.py +5 -3
  35. pyinfra/facts/mysql.py +1 -0
  36. pyinfra/facts/postgres.py +168 -0
  37. pyinfra/facts/postgresql.py +5 -161
  38. pyinfra/facts/selinux.py +3 -1
  39. pyinfra/facts/server.py +77 -30
  40. pyinfra/facts/systemd.py +29 -12
  41. pyinfra/facts/sysvinit.py +10 -10
  42. pyinfra/facts/util/packaging.py +4 -2
  43. pyinfra/local.py +4 -5
  44. pyinfra/operations/apk.py +3 -3
  45. pyinfra/operations/apt.py +25 -47
  46. pyinfra/operations/brew.py +7 -14
  47. pyinfra/operations/bsdinit.py +4 -4
  48. pyinfra/operations/cargo.py +1 -1
  49. pyinfra/operations/choco.py +1 -1
  50. pyinfra/operations/dnf.py +4 -4
  51. pyinfra/operations/files.py +108 -321
  52. pyinfra/operations/gem.py +1 -1
  53. pyinfra/operations/git.py +6 -37
  54. pyinfra/operations/iptables.py +2 -10
  55. pyinfra/operations/launchd.py +1 -1
  56. pyinfra/operations/lxd.py +1 -9
  57. pyinfra/operations/mysql.py +5 -28
  58. pyinfra/operations/npm.py +1 -1
  59. pyinfra/operations/openrc.py +1 -1
  60. pyinfra/operations/pacman.py +3 -3
  61. pyinfra/operations/pip.py +14 -15
  62. pyinfra/operations/pkg.py +1 -1
  63. pyinfra/operations/pkgin.py +3 -3
  64. pyinfra/operations/postgres.py +347 -0
  65. pyinfra/operations/postgresql.py +17 -380
  66. pyinfra/operations/python.py +2 -17
  67. pyinfra/operations/selinux.py +5 -28
  68. pyinfra/operations/server.py +59 -84
  69. pyinfra/operations/snap.py +1 -3
  70. pyinfra/operations/ssh.py +8 -23
  71. pyinfra/operations/systemd.py +7 -7
  72. pyinfra/operations/sysvinit.py +3 -12
  73. pyinfra/operations/upstart.py +4 -4
  74. pyinfra/operations/util/__init__.py +12 -0
  75. pyinfra/operations/util/files.py +2 -2
  76. pyinfra/operations/util/packaging.py +6 -24
  77. pyinfra/operations/util/service.py +18 -37
  78. pyinfra/operations/vzctl.py +2 -2
  79. pyinfra/operations/xbps.py +3 -3
  80. pyinfra/operations/yum.py +4 -4
  81. pyinfra/operations/zypper.py +4 -4
  82. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
  83. pyinfra-3.0b1.dist-info/RECORD +163 -0
  84. pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
  85. pyinfra_cli/__main__.py +2 -0
  86. pyinfra_cli/commands.py +7 -2
  87. pyinfra_cli/exceptions.py +83 -42
  88. pyinfra_cli/inventory.py +19 -4
  89. pyinfra_cli/log.py +17 -3
  90. pyinfra_cli/main.py +133 -90
  91. pyinfra_cli/prints.py +93 -129
  92. pyinfra_cli/util.py +60 -29
  93. tests/test_api/test_api.py +2 -0
  94. tests/test_api/test_api_arguments.py +13 -13
  95. tests/test_api/test_api_deploys.py +28 -29
  96. tests/test_api/test_api_facts.py +60 -98
  97. tests/test_api/test_api_operations.py +100 -200
  98. tests/test_cli/test_cli.py +18 -49
  99. tests/test_cli/test_cli_deploy.py +11 -37
  100. tests/test_cli/test_cli_exceptions.py +50 -19
  101. tests/test_cli/util.py +1 -1
  102. tests/test_connectors/test_chroot.py +6 -6
  103. tests/test_connectors/test_docker.py +4 -4
  104. tests/test_connectors/test_dockerssh.py +38 -50
  105. tests/test_connectors/test_local.py +11 -12
  106. tests/test_connectors/test_ssh.py +66 -107
  107. tests/test_connectors/test_terraform.py +9 -15
  108. tests/test_connectors/test_util.py +24 -46
  109. tests/test_connectors/test_vagrant.py +4 -4
  110. pyinfra/api/operation.pyi +0 -117
  111. pyinfra/connectors/ansible.py +0 -171
  112. pyinfra/connectors/mech.py +0 -186
  113. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  114. pyinfra/connectors/winrm.py +0 -320
  115. pyinfra/facts/windows.py +0 -366
  116. pyinfra/facts/windows_files.py +0 -90
  117. pyinfra/operations/windows.py +0 -59
  118. pyinfra/operations/windows_files.py +0 -551
  119. pyinfra-2.9.2.dist-info/RECORD +0 -170
  120. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  121. tests/test_connectors/test_ansible.py +0 -64
  122. tests/test_connectors/test_mech.py +0 -126
  123. tests/test_connectors/test_winrm.py +0 -76
  124. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
  125. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
  126. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/api/facts.py CHANGED
@@ -8,10 +8,23 @@ it's possible to call facts on hosts out of context (ie give me the IP of this
8
8
  other host B while I operate on this host A).
9
9
  """
10
10
 
11
+ from __future__ import annotations
12
+
11
13
  import re
12
14
  from inspect import getcallargs
13
15
  from socket import error as socket_error, timeout as timeout_error
14
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Type, Union
16
+ from typing import (
17
+ TYPE_CHECKING,
18
+ Any,
19
+ Callable,
20
+ Generic,
21
+ Iterable,
22
+ Optional,
23
+ Type,
24
+ TypeVar,
25
+ Union,
26
+ cast,
27
+ )
15
28
 
16
29
  import click
17
30
  import gevent
@@ -27,11 +40,11 @@ from pyinfra.api.util import (
27
40
  make_hash,
28
41
  print_host_combined_output,
29
42
  )
30
- from pyinfra.connectors.util import split_combined_output
43
+ from pyinfra.connectors.util import CommandOutput
31
44
  from pyinfra.context import ctx_host, ctx_state
32
45
  from pyinfra.progress import progress_spinner
33
46
 
34
- from .arguments import get_executor_kwarg_keys
47
+ from .arguments import CONNECTOR_ARGUMENT_KEYS
35
48
 
36
49
  if TYPE_CHECKING:
37
50
  from pyinfra.api.host import Host
@@ -44,14 +57,10 @@ SU_REGEXES = (
44
57
  )
45
58
 
46
59
 
47
- class FactNameMeta(type):
48
- def __init__(cls, name: str, bases, attrs, **kwargs):
49
- super().__init__(name, bases, attrs, **kwargs)
50
- module_name = cls.__module__.replace("pyinfra.facts.", "")
51
- cls.name = f"{module_name}.{cls.__name__}"
60
+ T = TypeVar("T")
52
61
 
53
62
 
54
- class FactBase(metaclass=FactNameMeta):
63
+ class FactBase(Generic[T]):
55
64
  name: str
56
65
 
57
66
  abstract: bool = True
@@ -62,23 +71,36 @@ class FactBase(metaclass=FactNameMeta):
62
71
 
63
72
  command: Union[str, Callable]
64
73
 
74
+ def __init_subclass__(cls) -> None:
75
+ super().__init_subclass__()
76
+ module_name = cls.__module__.replace("pyinfra.facts.", "")
77
+ cls.name = f"{module_name}.{cls.__name__}"
78
+
65
79
  @staticmethod
66
- def default():
80
+ def default() -> T:
67
81
  """
68
82
  Set the default attribute to be a type (eg list/dict).
69
83
  """
70
84
 
71
- @staticmethod
72
- def process(output):
73
- return "\n".join(output)
85
+ return cast(T, None)
86
+
87
+ def process(self, output: Iterable[str]) -> T:
88
+ # NOTE: TypeVar does not support a default, so we have to cast this str -> T
89
+ return cast(T, "\n".join(output))
74
90
 
75
91
  def process_pipeline(self, args, output):
76
92
  return {arg: self.process([output[i]]) for i, arg in enumerate(args)}
77
93
 
78
94
 
79
- class ShortFactBase(metaclass=FactNameMeta):
95
+ class ShortFactBase(Generic[T]):
96
+ name: str
80
97
  fact: Type[FactBase]
81
98
 
99
+ def __init_subclass__(cls) -> None:
100
+ super().__init_subclass__()
101
+ module_name = cls.__module__.replace("pyinfra.facts.", "")
102
+ cls.name = f"{module_name}.{cls.__name__}"
103
+
82
104
  @staticmethod
83
105
  def process_data(data):
84
106
  return data
@@ -99,8 +121,8 @@ def _make_command(command_attribute, host_args):
99
121
  def _get_executor_kwargs(
100
122
  state: "State",
101
123
  host: "Host",
102
- override_kwargs: Optional[Dict[str, Any]] = None,
103
- override_kwarg_keys: Optional[List[str]] = None,
124
+ override_kwargs: Optional[dict[str, Any]] = None,
125
+ override_kwarg_keys: Optional[list[str]] = None,
104
126
  ):
105
127
  if override_kwargs is None:
106
128
  override_kwargs = {}
@@ -108,7 +130,7 @@ def _get_executor_kwargs(
108
130
  override_kwarg_keys = []
109
131
 
110
132
  # Use the current operation global kwargs, or generate defaults
111
- global_kwargs = host.current_op_global_kwargs
133
+ global_kwargs = host.current_op_global_arguments
112
134
  if not global_kwargs:
113
135
  global_kwargs, _ = pop_global_arguments({}, state, host)
114
136
 
@@ -117,9 +139,7 @@ def _get_executor_kwargs(
117
139
  {key: value for key, value in global_kwargs.items() if key not in override_kwarg_keys},
118
140
  )
119
141
 
120
- return {
121
- key: value for key, value in override_kwargs.items() if key in get_executor_kwarg_keys()
122
- }
142
+ return {key: value for key, value in override_kwargs.items() if key in CONNECTOR_ARGUMENT_KEYS}
123
143
 
124
144
 
125
145
  def _handle_fact_kwargs(state, host, cls, args, kwargs):
@@ -136,13 +156,13 @@ def _handle_fact_kwargs(state, host, cls, args, kwargs):
136
156
  kwargs,
137
157
  state=state,
138
158
  host=host,
139
- keys_to_check=get_executor_kwarg_keys(),
159
+ keys_to_check=CONNECTOR_ARGUMENT_KEYS,
140
160
  )
141
161
 
142
162
  executor_kwargs = _get_executor_kwargs(
143
163
  state,
144
164
  host,
145
- override_kwargs=override_kwargs,
165
+ override_kwargs=override_kwargs, # type: ignore[arg-type]
146
166
  override_kwarg_keys=override_kwarg_keys,
147
167
  )
148
168
 
@@ -181,14 +201,12 @@ def get_facts(state: "State", *args, **kwargs):
181
201
  def get_fact(
182
202
  state: "State",
183
203
  host: "Host",
184
- cls: Type[FactBase],
204
+ cls: type[FactBase],
185
205
  args: Optional[Any] = None,
186
206
  kwargs: Optional[Any] = None,
187
207
  ensure_hosts: Optional[Any] = None,
188
208
  apply_failed_hosts: bool = True,
189
- fact_hash: Optional[Any] = None,
190
- use_cache: bool = True,
191
- ):
209
+ ) -> Any:
192
210
  if issubclass(cls, ShortFactBase):
193
211
  return get_short_facts(
194
212
  state,
@@ -198,36 +216,28 @@ def get_fact(
198
216
  kwargs=kwargs,
199
217
  ensure_hosts=ensure_hosts,
200
218
  apply_failed_hosts=apply_failed_hosts,
201
- fact_hash=fact_hash,
202
- use_cache=use_cache,
203
219
  )
204
220
 
205
- with host.facts_lock:
206
- if use_cache and fact_hash and fact_hash in host.facts:
207
- return host.facts[fact_hash]
208
-
209
- return _get_fact(
210
- state,
211
- host,
212
- cls,
213
- args,
214
- kwargs,
215
- ensure_hosts,
216
- apply_failed_hosts,
217
- fact_hash,
218
- )
221
+ return _get_fact(
222
+ state,
223
+ host,
224
+ cls,
225
+ args,
226
+ kwargs,
227
+ ensure_hosts,
228
+ apply_failed_hosts,
229
+ )
219
230
 
220
231
 
221
232
  def _get_fact(
222
233
  state: "State",
223
234
  host: "Host",
224
- cls: Type[FactBase],
225
- args: Optional[List] = None,
226
- kwargs: Optional[Dict] = None,
235
+ cls: type[FactBase],
236
+ args: Optional[list] = None,
237
+ kwargs: Optional[dict] = None,
227
238
  ensure_hosts: Optional[Any] = None,
228
239
  apply_failed_hosts: bool = True,
229
- fact_hash: Optional[Any] = None,
230
- ):
240
+ ) -> Any:
231
241
  fact = cls()
232
242
  name = fact.name
233
243
 
@@ -247,14 +257,15 @@ def _get_fact(
247
257
  raise_exceptions=True,
248
258
  )
249
259
 
250
- ignore_errors = (host.current_op_global_kwargs or {}).get(
251
- "ignore_errors",
252
- state.config.IGNORE_ERRORS,
260
+ ignore_errors = (
261
+ host.current_op_global_arguments["_ignore_errors"]
262
+ if host.in_op and host.current_op_global_arguments
263
+ else state.config.IGNORE_ERRORS
253
264
  )
254
265
 
255
266
  # Facts can override the shell (winrm powershell vs cmd support)
256
267
  if fact.shell_executable:
257
- executor_kwargs["shell_executable"] = fact.shell_executable
268
+ executor_kwargs["_shell_executable"] = fact.shell_executable
258
269
 
259
270
  command = _make_command(fact.command, fact_kwargs)
260
271
  requires_command = _make_command(fact.requires_command, fact_kwargs)
@@ -271,40 +282,38 @@ def _get_fact(
271
282
  )
272
283
 
273
284
  status = False
274
- stdout = []
275
- combined_output_lines = []
285
+ output = CommandOutput([])
276
286
 
277
287
  try:
278
- status, combined_output_lines = host.run_shell_command(
288
+ status, output = host.run_shell_command(
279
289
  command,
280
290
  print_output=state.print_fact_output,
281
291
  print_input=state.print_fact_input,
282
- return_combined_output=True,
283
292
  **executor_kwargs,
284
293
  )
285
294
  except (timeout_error, socket_error, SSHException) as e:
286
295
  log_host_command_error(
287
296
  host,
288
297
  e,
289
- timeout=executor_kwargs["timeout"],
298
+ timeout=executor_kwargs["_timeout"],
290
299
  )
291
300
 
292
- stdout, stderr = split_combined_output(combined_output_lines)
301
+ stdout_lines, stderr_lines = output.stdout_lines, output.stderr_lines
293
302
 
294
303
  data = fact.default()
295
304
 
296
305
  if status:
297
- if stdout:
298
- data = fact.process(stdout)
299
- elif stderr:
306
+ if stdout_lines:
307
+ data = fact.process(stdout_lines)
308
+ elif stderr_lines:
300
309
  # If we have error output and that error is sudo or su stating the user
301
310
  # does not exist, do not fail but instead return the default fact value.
302
311
  # This allows for users that don't currently but may be created during
303
312
  # other operations.
304
- first_line = stderr[0]
305
- if executor_kwargs["sudo_user"] and re.match(SUDO_REGEX, first_line):
313
+ first_line = stderr_lines[0]
314
+ if executor_kwargs["_sudo_user"] and re.match(SUDO_REGEX, first_line):
306
315
  status = True
307
- if executor_kwargs["su_user"] and any(re.match(regex, first_line) for regex in SU_REGEXES):
316
+ if executor_kwargs["_su_user"] and any(re.match(regex, first_line) for regex in SU_REGEXES):
308
317
  status = True
309
318
 
310
319
  if status:
@@ -321,7 +330,7 @@ def _get_fact(
321
330
  logger.debug(log_message)
322
331
  else:
323
332
  if not state.print_fact_output:
324
- print_host_combined_output(host, combined_output_lines)
333
+ print_host_combined_output(host, output)
325
334
 
326
335
  log_error_or_warning(
327
336
  host,
@@ -333,8 +342,6 @@ def _get_fact(
333
342
  if not status and not ignore_errors and apply_failed_hosts:
334
343
  state.fail_hosts({host})
335
344
 
336
- if fact_hash:
337
- host.facts[fact_hash] = data
338
345
  return data
339
346
 
340
347
 
@@ -349,50 +356,7 @@ def get_host_fact(
349
356
  state: "State",
350
357
  host: "Host",
351
358
  cls,
352
- args: Optional[List] = None,
353
- kwargs: Optional[Dict] = None,
354
- ):
355
- fact_hash = _get_fact_hash(state, host, cls, args, kwargs)
356
- return get_fact(state, host, cls, args=args, kwargs=kwargs, fact_hash=fact_hash)
357
-
358
-
359
- def reload_host_fact(
360
- state: "State",
361
- host: "Host",
362
- cls,
363
- args: Optional[List] = None,
364
- kwargs: Optional[Dict] = None,
365
- ):
366
- fact_hash = _get_fact_hash(state, host, cls, args, kwargs)
367
- return get_fact(
368
- state,
369
- host,
370
- cls,
371
- args=args,
372
- kwargs=kwargs,
373
- fact_hash=fact_hash,
374
- use_cache=False,
375
- )
376
-
377
-
378
- def create_host_fact(
379
- state: "State",
380
- host: "Host",
381
- cls,
382
- data,
383
- args: Optional[List] = None,
384
- kwargs: Optional[Dict] = None,
385
- ):
386
- fact_hash = _get_fact_hash(state, host, cls, args, kwargs)
387
- host.facts[fact_hash] = data
388
-
389
-
390
- def delete_host_fact(
391
- state: "State",
392
- host: "Host",
393
- cls,
394
- args: Optional[List] = None,
395
- kwargs: Optional[Dict] = None,
396
- ):
397
- fact_hash = _get_fact_hash(state, host, cls, args, kwargs)
398
- host.facts.pop(fact_hash, None)
359
+ args: Optional[Iterable] = None,
360
+ kwargs: Optional[dict] = None,
361
+ ) -> Any:
362
+ return get_fact(state, host, cls, args=args, kwargs=kwargs)