pyinfra 3.0b0__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 (45) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +5 -4
  3. pyinfra/api/arguments_typed.py +12 -2
  4. pyinfra/api/exceptions.py +19 -0
  5. pyinfra/api/facts.py +1 -1
  6. pyinfra/api/host.py +46 -7
  7. pyinfra/api/operation.py +77 -39
  8. pyinfra/api/operations.py +10 -11
  9. pyinfra/api/state.py +11 -2
  10. pyinfra/connectors/base.py +1 -1
  11. pyinfra/connectors/chroot.py +5 -6
  12. pyinfra/connectors/docker.py +11 -10
  13. pyinfra/connectors/dockerssh.py +5 -4
  14. pyinfra/connectors/local.py +5 -5
  15. pyinfra/connectors/ssh.py +44 -23
  16. pyinfra/connectors/terraform.py +9 -6
  17. pyinfra/connectors/util.py +1 -1
  18. pyinfra/connectors/vagrant.py +6 -5
  19. pyinfra/facts/choco.py +1 -1
  20. pyinfra/facts/deb.py +2 -2
  21. pyinfra/facts/postgres.py +168 -0
  22. pyinfra/facts/postgresql.py +5 -164
  23. pyinfra/facts/systemd.py +26 -10
  24. pyinfra/operations/files.py +5 -3
  25. pyinfra/operations/iptables.py +6 -0
  26. pyinfra/operations/pip.py +5 -0
  27. pyinfra/operations/postgres.py +347 -0
  28. pyinfra/operations/postgresql.py +17 -336
  29. pyinfra/operations/systemd.py +5 -3
  30. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/METADATA +6 -6
  31. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/RECORD +44 -43
  32. pyinfra_cli/commands.py +3 -2
  33. pyinfra_cli/exceptions.py +5 -0
  34. pyinfra_cli/main.py +2 -0
  35. pyinfra_cli/prints.py +22 -104
  36. tests/test_api/test_api_deploys.py +5 -5
  37. tests/test_api/test_api_operations.py +4 -4
  38. tests/test_connectors/test_ssh.py +52 -0
  39. tests/test_connectors/test_terraform.py +11 -8
  40. tests/test_connectors/test_vagrant.py +3 -3
  41. pyinfra_cli/inventory_dsl.py +0 -23
  42. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
  43. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
  44. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/entry_points.txt +0 -0
  45. {pyinfra-3.0b0.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/api/__init__.py CHANGED
@@ -11,6 +11,9 @@ from .config import Config # noqa: F401 # pragma: no cover
11
11
  from .deploy import deploy # noqa: F401 # pragma: no cover
12
12
  from .exceptions import DeployError # noqa: F401 # pragma: no cover
13
13
  from .exceptions import ( # noqa: F401
14
+ FactError,
15
+ FactTypeError,
16
+ FactValueError,
14
17
  InventoryError,
15
18
  OperationError,
16
19
  OperationTypeError,
pyinfra/api/arguments.py CHANGED
@@ -6,6 +6,7 @@ from typing import (
6
6
  Callable,
7
7
  Generic,
8
8
  Iterable,
9
+ List,
9
10
  Mapping,
10
11
  Optional,
11
12
  TypeVar,
@@ -169,7 +170,7 @@ class MetaArguments(TypedDict):
169
170
  name: str
170
171
  _ignore_errors: bool
171
172
  _continue_on_error: bool
172
- _if: Callable[[], bool]
173
+ _if: List[Callable[[], bool]]
173
174
 
174
175
 
175
176
  meta_argument_meta: dict[str, ArgumentMeta] = {
@@ -190,8 +191,8 @@ meta_argument_meta: dict[str, ArgumentMeta] = {
190
191
  default=lambda _: False,
191
192
  ),
192
193
  "_if": ArgumentMeta(
193
- "Only run this operation if this function returns True",
194
- default=lambda _: None,
194
+ "Only run this operation if these functions returns True",
195
+ default=lambda _: [],
195
196
  ),
196
197
  }
197
198
 
@@ -299,7 +300,7 @@ def pop_global_arguments(
299
300
  if context.ctx_config.isset():
300
301
  config = context.config
301
302
 
302
- meta_kwargs = host.current_deploy_kwargs or {}
303
+ meta_kwargs: dict[str, Any] = host.current_deploy_kwargs or {} # type: ignore[assignment]
303
304
 
304
305
  arguments: dict[str, Any] = {}
305
306
  found_keys: list[str] = []
@@ -1,6 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Callable, Generator, Generic, Iterable, Mapping, Optional, Union
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Callable,
6
+ Generator,
7
+ Generic,
8
+ Iterable,
9
+ List,
10
+ Mapping,
11
+ Optional,
12
+ Union,
13
+ )
4
14
 
5
15
  from typing_extensions import ParamSpec, Protocol
6
16
 
@@ -51,7 +61,7 @@ class PyinfraOperation(Generic[P], Protocol):
51
61
  name: Optional[str] = None,
52
62
  _ignore_errors: bool = False,
53
63
  _continue_on_error: bool = False,
54
- _if: Optional[Callable[[], bool]] = None,
64
+ _if: Optional[List[Callable[[], bool]]] = None,
55
65
  #
56
66
  # ExecutionArguments
57
67
  #
pyinfra/api/exceptions.py CHANGED
@@ -10,6 +10,25 @@ class ConnectError(PyinfraError):
10
10
  """
11
11
 
12
12
 
13
+ class FactError(PyinfraError):
14
+ """
15
+ Exception raised during fact gathering staging if a fact is unable to
16
+ generate output/change state.
17
+ """
18
+
19
+
20
+ class FactTypeError(FactError, TypeError):
21
+ """
22
+ Exception raised when a fact is passed invalid argument types.
23
+ """
24
+
25
+
26
+ class FactValueError(FactError, ValueError):
27
+ """
28
+ Exception raised when a fact is passed invalid argument values.
29
+ """
30
+
31
+
13
32
  class OperationError(PyinfraError):
14
33
  """
15
34
  Exception raised during fact gathering staging if an operation is unable to
pyinfra/api/facts.py CHANGED
@@ -50,7 +50,7 @@ if TYPE_CHECKING:
50
50
  from pyinfra.api.host import Host
51
51
  from pyinfra.api.state import State
52
52
 
53
- SUDO_REGEX = r"^sudo: unknown user:"
53
+ SUDO_REGEX = r"^sudo: unknown user"
54
54
  SU_REGEXES = (
55
55
  r"^su: user .+ does not exist",
56
56
  r"^su: unknown login",
pyinfra/api/host.py CHANGED
@@ -1,10 +1,22 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from contextlib import contextmanager
4
- from typing import TYPE_CHECKING, Any, Callable, Generator, Optional, Type, TypeVar, Union, overload
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ Callable,
8
+ Generator,
9
+ Optional,
10
+ Type,
11
+ TypeVar,
12
+ Union,
13
+ cast,
14
+ overload,
15
+ )
5
16
  from uuid import uuid4
6
17
 
7
18
  import click
19
+ from typing_extensions import Unpack
8
20
 
9
21
  from pyinfra import logger
10
22
  from pyinfra.connectors.base import BaseConnector
@@ -98,11 +110,16 @@ class Host:
98
110
  current_op_hash: Optional[str] = None
99
111
  current_op_global_arguments: Optional["AllArguments"] = None
100
112
 
101
- # Current context inside a @deploy function (op gen stage)
113
+ # Current context inside a @deploy function which become part of the op data
102
114
  in_deploy: bool = False
103
115
  current_deploy_name: Optional[str] = None
104
116
  current_deploy_kwargs = None
105
- current_deploy_data = None
117
+
118
+ # @deploy decorator data is a bit different - we need to handle the case
119
+ # where we're evaluating an operation at runtime (current_op_) but also
120
+ # when ordering operations (current_) outside of an operation context.
121
+ current_op_deploy_data: Optional[dict[str, Any]] = None
122
+ current_deploy_data: Optional[dict[str, Any]] = None
106
123
 
107
124
  # Current context during operation execution
108
125
  executing_op_hash: Optional[str] = None
@@ -204,9 +221,7 @@ class Host:
204
221
  self.log(message_styled, log_func=log_func)
205
222
 
206
223
  def get_deploy_data(self):
207
- if self.current_deploy_data:
208
- return self.current_deploy_data
209
- return {}
224
+ return self.current_op_deploy_data or self.current_deploy_data or {}
210
225
 
211
226
  def noop(self, description):
212
227
  """
@@ -216,8 +231,25 @@ class Host:
216
231
  handler = logger.info if self.state.print_noop_info else logger.debug
217
232
  handler("{0}noop: {1}".format(self.print_prefix, description))
218
233
 
234
+ def when(self, condition: Callable[[], bool]):
235
+ return self.deploy(
236
+ "",
237
+ cast("AllArguments", {"_if": [condition]}),
238
+ {},
239
+ in_deploy=False,
240
+ )
241
+
242
+ def arguments(self, **arguments: Unpack["AllArguments"]):
243
+ return self.deploy("", arguments, {}, in_deploy=False)
244
+
219
245
  @contextmanager
220
- def deploy(self, name: str, kwargs, data, in_deploy: bool = True):
246
+ def deploy(
247
+ self,
248
+ name: str,
249
+ kwargs: Optional["AllArguments"],
250
+ data: Optional[dict],
251
+ in_deploy: bool = True,
252
+ ):
221
253
  """
222
254
  Wraps a group of operations as a deploy, this should not be used
223
255
  directly, instead use ``pyinfra.api.deploy.deploy``.
@@ -234,6 +266,13 @@ class Host:
234
266
  old_deploy_data = self.current_deploy_data
235
267
  self.in_deploy = in_deploy
236
268
 
269
+ # Combine any old _ifs with the new ones
270
+ if old_deploy_kwargs and kwargs:
271
+ old_ifs = old_deploy_kwargs["_if"]
272
+ new_ifs = kwargs["_if"]
273
+ if old_ifs and new_ifs:
274
+ kwargs["_if"] = old_ifs + new_ifs
275
+
237
276
  # Set the new values
238
277
  self.current_deploy_name = name
239
278
  self.current_deploy_kwargs = kwargs
pyinfra/api/operation.py CHANGED
@@ -11,7 +11,7 @@ from functools import wraps
11
11
  from inspect import signature
12
12
  from io import StringIO
13
13
  from types import FunctionType
14
- from typing import Any, Callable, Generator, Iterator, Optional, cast
14
+ from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator, Optional, cast
15
15
 
16
16
  from typing_extensions import ParamSpec
17
17
 
@@ -36,16 +36,19 @@ from .util import (
36
36
 
37
37
  op_meta_default = object()
38
38
 
39
+ if TYPE_CHECKING:
40
+ from pyinfra.connectors.util import CommandOutput
41
+
39
42
 
40
43
  class OperationMeta:
41
44
  _hash: str
42
45
 
43
- _combined_output_lines = None
46
+ _combined_output: Optional["CommandOutput"] = None
44
47
  _commands: Optional[list[Any]] = None
45
- _maybe_is_change: bool = False
48
+ _maybe_is_change: Optional[bool] = False
46
49
  _success: Optional[bool] = None
47
50
 
48
- def __init__(self, hash, is_change=False):
51
+ def __init__(self, hash, is_change: Optional[bool]):
49
52
  self._hash = hash
50
53
  self._maybe_is_change = is_change
51
54
 
@@ -57,7 +60,7 @@ class OperationMeta:
57
60
  if self._commands is not None:
58
61
  return (
59
62
  "OperationMeta(executed=True, "
60
- f"changed={self.did_change()}, hash={self._hash}, commands={len(self._commands)})"
63
+ f"success={self.did_succeed}, hash={self._hash}, commands={len(self._commands)})"
61
64
  )
62
65
  return (
63
66
  "OperationMeta(executed=False, "
@@ -69,13 +72,13 @@ class OperationMeta:
69
72
  self,
70
73
  success: bool,
71
74
  commands: list[Any],
72
- combined_output_lines,
75
+ combined_output: "CommandOutput",
73
76
  ) -> None:
74
77
  if self.is_complete():
75
78
  raise RuntimeError("Cannot complete an already complete operation")
76
79
  self._success = success
77
80
  self._commands = commands
78
- self._combined_output_lines = combined_output_lines
81
+ self._combined_output = combined_output
79
82
 
80
83
  def is_complete(self) -> bool:
81
84
  return self._success is not None
@@ -84,10 +87,17 @@ class OperationMeta:
84
87
  if not self.is_complete():
85
88
  raise RuntimeError("Cannot evaluate operation result before execution")
86
89
 
87
- def did_change(self) -> bool:
88
- self._raise_if_not_complete()
90
+ def _did_change(self) -> bool:
89
91
  return bool(self._success and len(self._commands or []) > 0)
90
92
 
93
+ @property
94
+ def did_change(self):
95
+ return context.host.when(self._did_change)
96
+
97
+ @property
98
+ def did_not_change(self):
99
+ return context.host.when(lambda: not self._did_change())
100
+
91
101
  def did_succeed(self) -> bool:
92
102
  self._raise_if_not_complete()
93
103
  return self._success is True
@@ -96,19 +106,32 @@ class OperationMeta:
96
106
  self._raise_if_not_complete()
97
107
  return self._success is False
98
108
 
99
- # Output lines
100
- def _get_lines(self, types=("stdout", "stderr")):
101
- self._raise_if_not_complete()
102
- assert self._combined_output_lines is not None
103
- return [line for type_, line in self._combined_output_lines if type_ in types]
109
+ # TODO: deprecated, remove in v4
110
+ @property
111
+ def changed(self) -> bool:
112
+ if self.is_complete():
113
+ return self._did_change()
114
+
115
+ if self._maybe_is_change is not None:
116
+ return self._maybe_is_change
117
+
118
+ op_data = context.state.get_op_data_for_host(context.host, self._hash)
119
+ cmd_gen = op_data.command_generator
120
+ for _ in cmd_gen():
121
+ return True
122
+ return False
104
123
 
105
124
  @property
106
- def stdout_lines(self):
107
- return self._get_lines(types=("stdout",))
125
+ def stdout_lines(self) -> list[str]:
126
+ self._raise_if_not_complete()
127
+ assert self._combined_output is not None
128
+ return self._combined_output.stdout_lines
108
129
 
109
130
  @property
110
- def stderr_lines(self):
111
- return self._get_lines(types=("stderr",))
131
+ def stderr_lines(self) -> list[str]:
132
+ self._raise_if_not_complete()
133
+ assert self._combined_output is not None
134
+ return self._combined_output.stderr_lines
112
135
 
113
136
  @property
114
137
  def stdout(self) -> str:
@@ -118,14 +141,6 @@ class OperationMeta:
118
141
  def stderr(self) -> str:
119
142
  return "\n".join(self.stderr_lines)
120
143
 
121
- # TODO: deprecated, remove in v4
122
- @property
123
- def changed(self) -> int:
124
- if not self.is_complete():
125
- logger.warning("Checking changed before execution can give unexpected results")
126
- return self._maybe_is_change
127
- return self.did_change()
128
-
129
144
 
130
145
  def add_op(state: State, op_func, *args, **kwargs):
131
146
  """
@@ -164,6 +179,8 @@ P = ParamSpec("P")
164
179
  def operation(
165
180
  is_idempotent: bool = True,
166
181
  idempotent_notice: Optional[str] = None,
182
+ is_deprecated: bool = False,
183
+ deprecated_for: Optional[str] = None,
167
184
  _set_in_op: bool = True,
168
185
  ) -> Callable[[Callable[P, Generator]], PyinfraOperation[P]]:
169
186
  """
@@ -175,6 +192,8 @@ def operation(
175
192
  def decorator(f: Callable[P, Generator]) -> PyinfraOperation[P]:
176
193
  f.is_idempotent = is_idempotent # type: ignore[attr-defined]
177
194
  f.idempotent_notice = idempotent_notice # type: ignore[attr-defined]
195
+ f.is_deprecated = is_deprecated # type: ignore[attr-defined]
196
+ f.deprecated_for = deprecated_for # type: ignore[attr-defined]
178
197
  return _wrap_operation(f, _set_in_op=_set_in_op)
179
198
 
180
199
  return decorator
@@ -192,6 +211,15 @@ def _wrap_operation(func: Callable[P, Generator], _set_in_op: bool = True) -> Py
192
211
  + "function to call the underlying operation."
193
212
  )
194
213
 
214
+ if func.is_deprecated: # type: ignore[attr-defined]
215
+ if func.deprecated_for: # type: ignore[attr-defined]
216
+ logger.warning(
217
+ f"The {get_operation_name_from_func(func)} operation is "
218
+ + f"deprecated, please use: {func.deprecated_for}", # type: ignore[attr-defined] # noqa
219
+ )
220
+ else:
221
+ logger.warning(f"The {get_operation_name_from_func(func)} operation is deprecated")
222
+
195
223
  # Configure operation
196
224
  #
197
225
  # Get the meta kwargs (globals that apply to all hosts)
@@ -217,7 +245,12 @@ def _wrap_operation(func: Callable[P, Generator], _set_in_op: bool = True) -> Py
217
245
  break
218
246
 
219
247
  if has_run:
220
- return OperationMeta(op_hash)
248
+ return OperationMeta(op_hash, is_change=False)
249
+
250
+ # Grab a reference to any *current* deploy data as this may change when
251
+ # we later evaluate the operation at runtime.This means we put back the
252
+ # expected deploy data.
253
+ current_deploy_data = host.current_deploy_data
221
254
 
222
255
  # "Run" operation - here we make a generator that will yield out actual commands to execute
223
256
  # and, if we're diff-ing, we then iterate the generator now to determine if any changes
@@ -226,13 +259,14 @@ def _wrap_operation(func: Callable[P, Generator], _set_in_op: bool = True) -> Py
226
259
  def command_generator() -> Iterator[PyinfraCommand]:
227
260
  # Check global _if_ argument function and do nothing if returns False
228
261
  if state.is_executing:
229
- _if = global_arguments.get("_if")
230
- if _if and _if() is False:
262
+ _ifs = global_arguments.get("_if")
263
+ if _ifs and not all(_if() for _if in _ifs):
231
264
  return
232
265
 
233
266
  host.in_op = _set_in_op
234
267
  host.current_op_hash = op_hash
235
268
  host.current_op_global_arguments = global_arguments
269
+ host.current_op_deploy_data = current_deploy_data
236
270
 
237
271
  try:
238
272
  for command in func(*args, **kwargs):
@@ -243,10 +277,12 @@ def _wrap_operation(func: Callable[P, Generator], _set_in_op: bool = True) -> Py
243
277
  host.in_op = False
244
278
  host.current_op_hash = None
245
279
  host.current_op_global_arguments = None
280
+ host.current_op_deploy_data = None
246
281
 
247
- op_is_change = False
282
+ op_is_change = None
248
283
  if state.should_check_for_changes():
249
- for command in command_generator():
284
+ op_is_change = False
285
+ for _ in command_generator():
250
286
  op_is_change = True
251
287
  break
252
288
  else:
@@ -279,6 +315,15 @@ def _wrap_operation(func: Callable[P, Generator], _set_in_op: bool = True) -> Py
279
315
  return cast(PyinfraOperation[P], decorated_func)
280
316
 
281
317
 
318
+ def get_operation_name_from_func(func):
319
+ if func.__module__:
320
+ module_bits = func.__module__.split(".")
321
+ module_name = module_bits[-1]
322
+ return "{0}.{1}".format(module_name, func.__name__)
323
+ else:
324
+ return func.__name__
325
+
326
+
282
327
  def generate_operation_name(func, host, kwargs, global_arguments):
283
328
  # Generate an operation name if needed (Module/Operation format)
284
329
  name = global_arguments.get("name")
@@ -287,14 +332,7 @@ def generate_operation_name(func, host, kwargs, global_arguments):
287
332
  names = {name}
288
333
  else:
289
334
  add_args = True
290
-
291
- if func.__module__:
292
- module_bits = func.__module__.split(".")
293
- module_name = module_bits[-1]
294
- name = "{0}/{1}".format(module_name.title(), func.__name__.title())
295
- else:
296
- name = func.__name__
297
-
335
+ name = get_operation_name_from_func(func)
298
336
  names = {name}
299
337
 
300
338
  if host.current_deploy_name:
pyinfra/api/operations.py CHANGED
@@ -67,17 +67,16 @@ def _run_host_op(state: "State", host: "Host", op_hash: str) -> Optional[bool]:
67
67
  timeout = global_arguments.get("_timeout", 0)
68
68
 
69
69
  executor_kwarg_keys = CONNECTOR_ARGUMENT_KEYS
70
- base_connector_arguments: ConnectorArguments = (
71
- cast( # https://github.com/python/mypy/issues/10371
72
- ConnectorArguments,
73
- {key: global_arguments[key] for key in executor_kwarg_keys if key in global_arguments},
74
- )
70
+ # See: https://github.com/python/mypy/issues/10371
71
+ base_connector_arguments: ConnectorArguments = cast(
72
+ ConnectorArguments,
73
+ {key: global_arguments[key] for key in executor_kwarg_keys if key in global_arguments}, # type: ignore[literal-required] # noqa
75
74
  )
76
75
 
77
76
  did_error = False
78
77
  executed_commands = 0
79
78
  commands = []
80
- all_combined_output_lines: list[OutputLine] = []
79
+ all_output_lines: list[OutputLine] = []
81
80
 
82
81
  for command in op_data.command_generator():
83
82
  commands.append(command)
@@ -103,19 +102,19 @@ def _run_host_op(state: "State", host: "Host", op_hash: str) -> Optional[bool]:
103
102
  )
104
103
 
105
104
  elif isinstance(command, StringCommand):
106
- combined_output_lines = CommandOutput([])
105
+ output_lines = CommandOutput([])
107
106
  try:
108
- status, combined_output_lines = command.execute(
107
+ status, output_lines = command.execute(
109
108
  state,
110
109
  host,
111
110
  connector_arguments,
112
111
  )
113
112
  except (timeout_error, socket_error, SSHException) as e:
114
113
  log_host_command_error(host, e, timeout=timeout)
115
- all_combined_output_lines.extend(combined_output_lines)
114
+ all_output_lines.extend(output_lines)
116
115
  # If we failed and have not already printed the stderr, print it
117
116
  if status is False and not state.print_output:
118
- print_host_combined_output(host, combined_output_lines)
117
+ print_host_combined_output(host, output_lines)
119
118
 
120
119
  else:
121
120
  try:
@@ -170,7 +169,7 @@ def _run_host_op(state: "State", host: "Host", op_hash: str) -> Optional[bool]:
170
169
  op_data.operation_meta.set_complete(
171
170
  op_success,
172
171
  commands,
173
- all_combined_output_lines,
172
+ CommandOutput(all_output_lines),
174
173
  )
175
174
 
176
175
  return return_status
pyinfra/api/state.py CHANGED
@@ -344,10 +344,19 @@ class State:
344
344
  def get_results_for_host(self, host: "Host") -> StateHostResults:
345
345
  return self.results[host]
346
346
 
347
- def get_op_data_for_host(self, host: "Host", op_hash: str):
347
+ def get_op_data_for_host(
348
+ self,
349
+ host: "Host",
350
+ op_hash: str,
351
+ ) -> StateOperationHostData:
348
352
  return self.ops[host][op_hash]
349
353
 
350
- def set_op_data_for_host(self, host: "Host", op_hash: str, op_data: StateOperationHostData):
354
+ def set_op_data_for_host(
355
+ self,
356
+ host: "Host",
357
+ op_hash: str,
358
+ op_data: StateOperationHostData,
359
+ ):
351
360
  self.ops[host][op_hash] = op_data
352
361
 
353
362
  def activate_host(self, host: "Host"):
@@ -84,7 +84,7 @@ class BaseConnector(abc.ABC):
84
84
 
85
85
  @staticmethod
86
86
  @abc.abstractmethod
87
- def make_names_data(id: str) -> Iterator[tuple[str, dict, list[str]]]:
87
+ def make_names_data(name: str) -> Iterator[tuple[str, dict, list[str]]]:
88
88
  """
89
89
  Generates hosts/data/groups information for inventory. This allows a
90
90
  single connector reference to generate multiple target hosts.
@@ -40,17 +40,17 @@ class ChrootConnector(BaseConnector):
40
40
  self.local = LocalConnector(state, host)
41
41
 
42
42
  @staticmethod
43
- def make_names_data(directory: Optional[str] = None):
44
- if not directory:
43
+ def make_names_data(name: Optional[str] = None):
44
+ if not name:
45
45
  raise InventoryError("No directory provided!")
46
46
 
47
47
  show_warning()
48
48
 
49
- yield "@chroot/{0}".format(directory), {
50
- "chroot_directory": "/{0}".format(directory.lstrip("/")),
49
+ yield "@chroot/{0}".format(name), {
50
+ "chroot_directory": "/{0}".format(name.lstrip("/")),
51
51
  }, ["@chroot"]
52
52
 
53
- def connect(self):
53
+ def connect(self) -> None:
54
54
  self.local.connect()
55
55
 
56
56
  chroot_directory = self.host.data.chroot_directory
@@ -65,7 +65,6 @@ class ChrootConnector(BaseConnector):
65
65
  raise ConnectError(e.args[0])
66
66
 
67
67
  self.host.connector_data["chroot_directory"] = chroot_directory
68
- return True
69
68
 
70
69
  def run_shell_command(
71
70
  self,
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
24
24
  from pyinfra.api.state import State
25
25
 
26
26
 
27
- class ConnectorData(TypedDict, total=False):
27
+ class ConnectorData(TypedDict):
28
28
  docker_identifier: str
29
29
 
30
30
 
@@ -35,6 +35,7 @@ connector_data_meta: dict[str, DataMeta] = {
35
35
 
36
36
  def _find_start_docker_container(container_id) -> tuple[str, bool]:
37
37
  docker_info = local.shell("docker container inspect {0}".format(container_id))
38
+ assert isinstance(docker_info, str)
38
39
  docker_info = json.loads(docker_info)[0]
39
40
  if docker_info["State"]["Running"] is False:
40
41
  logger.info("Starting stopped container: {0}".format(container_id))
@@ -60,8 +61,10 @@ class DockerConnector(BaseConnector):
60
61
  The docker connector allows you to build Docker images or modify running
61
62
  Docker containers. You can pass either an image name or existing container ID:
62
63
 
63
- + Image - will create a new container from the image, execute operations against it, save into a new Docker image and remove the container
64
- + Existing container ID - will execute operations against the running container, leaving it running
64
+ + Image - will create a new container from the image, execute operations \
65
+ against it, save into a new Docker image and remove the container
66
+ + Existing container ID - will execute operations against the running \
67
+ container, leaving it running
65
68
 
66
69
  .. code:: shell
67
70
 
@@ -91,17 +94,17 @@ class DockerConnector(BaseConnector):
91
94
  self.local = LocalConnector(state, host)
92
95
 
93
96
  @staticmethod
94
- def make_names_data(identifier=None):
95
- if not identifier:
97
+ def make_names_data(name=None):
98
+ if not name:
96
99
  raise InventoryError("No docker base ID provided!")
97
100
 
98
101
  yield (
99
- "@docker/{0}".format(identifier),
100
- {"docker_identifier": identifier},
102
+ "@docker/{0}".format(name),
103
+ {"docker_identifier": name},
101
104
  ["@docker"],
102
105
  )
103
106
 
104
- def connect(self):
107
+ def connect(self) -> None:
105
108
  self.local.connect()
106
109
 
107
110
  docker_identifier = self.data["docker_identifier"]
@@ -113,8 +116,6 @@ class DockerConnector(BaseConnector):
113
116
  except PyinfraError:
114
117
  self.container_id = _start_docker_image(docker_identifier)
115
118
 
116
- return True
117
-
118
119
  def disconnect(self):
119
120
  container_id = self.container_id
120
121
 
@@ -30,7 +30,8 @@ class DockerSSHConnector(BaseConnector):
30
30
  """
31
31
  **Note**: this connector is in beta!
32
32
 
33
- The ``@dockerssh`` connector allows you to run commands on Docker containers on a remote machine.
33
+ The ``@dockerssh`` connector allows you to run commands on Docker containers \
34
+ on a remote machine.
34
35
 
35
36
  .. code:: shell
36
37
 
@@ -50,10 +51,10 @@ class DockerSSHConnector(BaseConnector):
50
51
  self.ssh = SSHConnector(state, host)
51
52
 
52
53
  @staticmethod
53
- def make_names_data(host_image_str):
54
+ def make_names_data(name):
54
55
  try:
55
- hostname, image = host_image_str.split(":", 1)
56
- except (AttributeError, ValueError): # failure to parse the host_image_str
56
+ hostname, image = name.split(":", 1)
57
+ except (AttributeError, ValueError): # failure to parse the name
57
58
  raise InventoryError("No ssh host or docker base image provided!")
58
59
 
59
60
  if not image: