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/host.py CHANGED
@@ -1,22 +1,39 @@
1
+ from __future__ import annotations
2
+
1
3
  from contextlib import contextmanager
2
- from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional, Union
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
+ )
16
+ from uuid import uuid4
3
17
 
4
18
  import click
5
- from gevent.lock import BoundedSemaphore
19
+ from typing_extensions import Unpack
6
20
 
7
21
  from pyinfra import logger
8
- from pyinfra.connectors.util import remove_any_sudo_askpass_file
22
+ from pyinfra.connectors.base import BaseConnector
23
+ from pyinfra.connectors.util import CommandOutput, remove_any_sudo_askpass_file
9
24
 
10
25
  from .connectors import get_execution_connector
11
26
  from .exceptions import ConnectError
12
- from .facts import create_host_fact, delete_host_fact, get_host_fact, reload_host_fact
27
+ from .facts import FactBase, ShortFactBase, get_host_fact
28
+ from .util import memoize, sha1_hash
13
29
 
14
30
  if TYPE_CHECKING:
31
+ from pyinfra.api.arguments import AllArguments
15
32
  from pyinfra.api.inventory import Inventory
16
33
  from pyinfra.api.state import State
17
34
 
18
35
 
19
- def extract_callable_datas(datas: List[Union[Callable[..., Any], Any]]) -> Generator[Any, Any, Any]:
36
+ def extract_callable_datas(datas: list[Union[Callable[..., Any], Any]]) -> Generator[Any, Any, Any]:
20
37
  for data in datas:
21
38
  # Support for dynamic data, ie @deploy wrapped data defaults where
22
39
  # the data is stored on the state temporarily.
@@ -31,7 +48,7 @@ class HostData:
31
48
  Combines multiple AttrData's to search for attributes.
32
49
  """
33
50
 
34
- override_datas: Dict[str, Any]
51
+ override_datas: dict[str, Any]
35
52
 
36
53
  def __init__(self, host: "Host", *datas):
37
54
  self.__dict__["host"] = host
@@ -82,28 +99,36 @@ class Host:
82
99
  data.
83
100
  """
84
101
 
85
- connection = None
86
102
  state: "State"
103
+ connector_cls: type[BaseConnector]
104
+ connector: BaseConnector
105
+ connected: bool = False
87
106
 
88
107
  # Current context inside an @operation function (op gen stage)
89
108
  in_op: bool = False
109
+ in_callback_op: bool = False
90
110
  current_op_hash: Optional[str] = None
91
- current_op_global_kwargs: Dict[str, Any]
111
+ current_op_global_arguments: Optional["AllArguments"] = None
92
112
 
93
- # Current context inside a @deploy function (op gen stage)
113
+ # Current context inside a @deploy function which become part of the op data
94
114
  in_deploy: bool = False
95
115
  current_deploy_name: Optional[str] = None
96
116
  current_deploy_kwargs = None
97
- 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
98
123
 
99
124
  # Current context during operation execution
100
- executing_op_hash = None
101
- nested_executing_op_hash = None
125
+ executing_op_hash: Optional[str] = None
126
+ nested_executing_op_hash: Optional[str] = None
102
127
 
103
- loop_position: List[int]
128
+ loop_position: list[int]
104
129
 
105
130
  # Arbitrary data dictionary connectors may use
106
- connector_data: Dict[str, Any]
131
+ connector_data: dict[str, Any]
107
132
 
108
133
  def loop(self, iterable):
109
134
  self.loop_position.append(0)
@@ -117,26 +142,20 @@ class Host:
117
142
  name: str,
118
143
  inventory: "Inventory",
119
144
  groups,
120
- executor=get_execution_connector("ssh"),
145
+ connector_cls=get_execution_connector("ssh"),
121
146
  ):
122
147
  self.inventory = inventory
123
148
  self.groups = groups
124
- self.executor = executor
149
+ self.connector_cls = connector_cls
125
150
  self.name = name
126
151
 
127
152
  self.loop_position = []
128
153
 
129
154
  self.connector_data = {}
130
- self.current_op_global_kwargs = {}
131
-
132
- # Fact data store
133
- # TODO: how to not have Any here?
134
- self.facts: Dict[str, Any] = {}
135
- self.facts_lock = BoundedSemaphore()
136
155
 
137
156
  # Append only list of operation hashes as called on this host, used to
138
157
  # generate a DAG to create the final operation order.
139
- self.op_hash_order: List[str] = []
158
+ self.op_hash_order: list[str] = []
140
159
 
141
160
  # Create the (waterfall data: override, host, group, global)
142
161
  self.data = HostData(
@@ -149,16 +168,20 @@ class Host:
149
168
  self.get_deploy_data,
150
169
  )
151
170
 
171
+ def init(self, state: "State") -> None:
172
+ self.state = state
173
+ self.connector = self.connector_cls(state, self)
174
+
175
+ longest_name_len = max([len(host.name) for host in self.inventory])
176
+ padding_diff = longest_name_len - len(self.name)
177
+ self.print_prefix_padding = "".join(" " for _ in range(0, padding_diff))
178
+
152
179
  def __str__(self):
153
180
  return "{0}".format(self.name)
154
181
 
155
182
  def __repr__(self):
156
183
  return "Host({0})".format(self.name)
157
184
 
158
- @property
159
- def connected(self) -> bool:
160
- return self.connection is not None
161
-
162
185
  @property
163
186
  def host_data(self):
164
187
  return self.inventory.get_host_data(self.name)
@@ -168,30 +191,37 @@ class Host:
168
191
  return self.inventory.get_groups_data(self.groups)
169
192
 
170
193
  @property
171
- def print_prefix(self):
194
+ def print_prefix(self) -> str:
172
195
  if self.nested_executing_op_hash:
173
- return "{0}[{1}] {2} ".format(
196
+ return "{0}[{1}] {2}{3} ".format(
174
197
  click.style(""), # reset
175
198
  click.style(self.name, bold=True),
176
199
  click.style("nested", "blue"),
200
+ self.print_prefix_padding,
177
201
  )
178
202
 
179
- return "{0}[{1}] ".format(
203
+ return "{0}[{1}]{2} ".format(
180
204
  click.style(""), # reset
181
205
  click.style(self.name, bold=True),
206
+ self.print_prefix_padding,
182
207
  )
183
208
 
184
- def style_print_prefix(self, *args, **kwargs):
185
- return "{0}[{1}] ".format(
209
+ def style_print_prefix(self, *args, **kwargs) -> str:
210
+ return "{0}[{1}]{2} ".format(
186
211
  click.style(""), # reset
187
212
  click.style(self.name, *args, **kwargs),
213
+ self.print_prefix_padding,
188
214
  )
189
215
 
190
- def get_deploy_data(self):
191
- if self.current_deploy_data:
192
- return self.current_deploy_data
216
+ def log(self, message, log_func=logger.info):
217
+ log_func(f"{self.print_prefix}{message}")
218
+
219
+ def log_styled(self, message, log_func=logger.info, **kwargs):
220
+ message_styled = click.style(message, **kwargs)
221
+ self.log(message_styled, log_func=log_func)
193
222
 
194
- return {}
223
+ def get_deploy_data(self):
224
+ return self.current_op_deploy_data or self.current_deploy_data or {}
195
225
 
196
226
  def noop(self, description):
197
227
  """
@@ -201,8 +231,25 @@ class Host:
201
231
  handler = logger.info if self.state.print_noop_info else logger.debug
202
232
  handler("{0}noop: {1}".format(self.print_prefix, description))
203
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
+
204
245
  @contextmanager
205
- 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
+ ):
206
253
  """
207
254
  Wraps a group of operations as a deploy, this should not be used
208
255
  directly, instead use ``pyinfra.api.deploy.deploy``.
@@ -219,6 +266,13 @@ class Host:
219
266
  old_deploy_data = self.current_deploy_data
220
267
  self.in_deploy = in_deploy
221
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
+
222
276
  # Set the new values
223
277
  self.current_deploy_name = name
224
278
  self.current_deploy_kwargs = kwargs
@@ -245,33 +299,55 @@ class Host:
245
299
  old_deploy_data,
246
300
  )
247
301
 
248
- # Host facts
249
- #
302
+ @memoize
303
+ def _get_temp_directory(self):
304
+ temp_directory = self.state.config.TEMP_DIR
250
305
 
251
- def get_fact(self, name_or_cls, *args, **kwargs):
252
- """
253
- Get a fact for this host, reading from the cache if present.
254
- """
255
- return get_host_fact(self.state, self, name_or_cls, args=args, kwargs=kwargs)
306
+ if temp_directory is None:
307
+ # Unfortunate, but very hard to avoid, circular dependency, this method is memoized so
308
+ # performance isn't a concern.
309
+ from pyinfra.facts.server import TmpDir
256
310
 
257
- def reload_fact(self, name_or_cls, *args, **kwargs):
258
- """
259
- Get a fact for this host without using any cached value, always re-fetch the fact data
260
- from the host and then cache it.
261
- """
262
- return reload_host_fact(self.state, self, name_or_cls, args=args, kwargs=kwargs)
311
+ temp_directory = self.get_fact(TmpDir)
263
312
 
264
- def create_fact(self, name_or_cls, data=None, kwargs=None):
313
+ if not temp_directory:
314
+ temp_directory = self.state.config.DEFAULT_TEMP_DIR
315
+
316
+ return temp_directory
317
+
318
+ def get_temp_filename(self, hash_key: Optional[str] = None, hash_filename: bool = True):
265
319
  """
266
- Create a new fact for this host in the fact cache.
320
+ Generate a temporary filename for this deploy.
267
321
  """
268
- return create_host_fact(self.state, self, name_or_cls, data, kwargs=kwargs)
269
322
 
270
- def delete_fact(self, name_or_cls, kwargs=None):
323
+ temp_directory = self._get_temp_directory()
324
+
325
+ if not hash_key:
326
+ hash_key = str(uuid4())
327
+
328
+ if hash_filename:
329
+ hash_key = sha1_hash(hash_key)
330
+
331
+ return "{0}/pyinfra-{1}".format(temp_directory, hash_key)
332
+
333
+ # Host facts
334
+ #
335
+
336
+ T = TypeVar("T")
337
+
338
+ @overload
339
+ def get_fact(self, name_or_cls: Type[FactBase[T]], *args, **kwargs) -> T:
340
+ ...
341
+
342
+ @overload
343
+ def get_fact(self, name_or_cls: Type[ShortFactBase[T]], *args, **kwargs) -> T:
344
+ ...
345
+
346
+ def get_fact(self, name_or_cls, *args, **kwargs):
271
347
  """
272
- Remove an existing fact for this host in the fact cache.
348
+ Get a fact for this host, reading from the cache if present.
273
349
  """
274
- return delete_host_fact(self.state, self, name_or_cls, kwargs=kwargs)
350
+ return get_host_fact(self.state, self, name_or_cls, args=args, kwargs=kwargs)
275
351
 
276
352
  # Connector proxy
277
353
  #
@@ -286,11 +362,11 @@ class Host:
286
362
  """
287
363
 
288
364
  self._check_state()
289
- if not self.connection:
365
+ if not self.connected:
290
366
  self.state.trigger_callbacks("host_before_connect", self)
291
367
 
292
368
  try:
293
- self.connection = self.executor.connect(self.state, self)
369
+ self.connector.connect()
294
370
  except ConnectError as e:
295
371
  if show_errors:
296
372
  log_message = "{0}{1}".format(
@@ -316,8 +392,7 @@ class Host:
316
392
 
317
393
  logger.info(log_message)
318
394
  self.state.trigger_callbacks("host_connect", self)
319
-
320
- return self.connection
395
+ self.connected = True
321
396
 
322
397
  def disconnect(self):
323
398
  """
@@ -325,50 +400,43 @@ class Host:
325
400
  """
326
401
  self._check_state()
327
402
 
328
- # Disconnect is an optional function for executors if needed
329
- disconnect_func = getattr(self.executor, "disconnect", None)
403
+ # Disconnect is an optional function for connectors if needed
404
+ disconnect_func = getattr(self.connector, "disconnect", None)
330
405
  if disconnect_func:
331
- return disconnect_func(self.state, self)
406
+ return disconnect_func()
332
407
 
333
408
  # TODO: consider whether this should be here!
334
409
  remove_any_sudo_askpass_file(self)
335
410
 
336
411
  self.state.trigger_callbacks("host_disconnect", self)
337
412
 
338
- def run_shell_command(self, *args, **kwargs):
413
+ def run_shell_command(self, *args, **kwargs) -> tuple[bool, CommandOutput]:
339
414
  """
340
415
  Low level method to execute a shell command on the host via it's configured connector.
341
416
  """
342
417
  self._check_state()
343
- return self.executor.run_shell_command(self.state, self, *args, **kwargs)
418
+ return self.connector.run_shell_command(*args, **kwargs)
344
419
 
345
- def put_file(self, *args, **kwargs):
420
+ def put_file(self, *args, **kwargs) -> bool:
346
421
  """
347
422
  Low level method to upload a file to the host via it's configured connector.
348
423
  """
349
424
  self._check_state()
350
- return self.executor.put_file(self.state, self, *args, **kwargs)
425
+ return self.connector.put_file(*args, **kwargs)
351
426
 
352
- def get_file(self, *args, **kwargs):
427
+ def get_file(self, *args, **kwargs) -> bool:
353
428
  """
354
429
  Low level method to download a file from the host via it's configured connector.
355
430
  """
356
431
  self._check_state()
357
- return self.executor.get_file(self.state, self, *args, **kwargs)
358
-
359
- # Rsync - optional executor specific ability
432
+ return self.connector.get_file(*args, **kwargs)
360
433
 
361
- def check_can_rsync(self):
362
- check_can_rsync_func = getattr(self.executor, "check_can_rsync", None)
363
- if check_can_rsync_func:
364
- return check_can_rsync_func(self)
434
+ # Rsync - optional connector specific ability
365
435
 
366
- raise NotImplementedError(
367
- "The {0} connector does not support rsync!".format(
368
- self.executor.__name__,
369
- ),
370
- )
436
+ def check_can_rsync(self) -> None:
437
+ self._check_state()
438
+ return self.connector.check_can_rsync()
371
439
 
372
- def rsync(self, *args, **kwargs):
440
+ def rsync(self, *args, **kwargs) -> bool:
373
441
  self._check_state()
374
- return self.executor.rsync(self.state, self, *args, **kwargs)
442
+ return self.connector.rsync(*args, **kwargs)
pyinfra/api/inventory.py CHANGED
@@ -1,5 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  from collections import defaultdict
2
- from typing import TYPE_CHECKING, Any, Iterator, List
4
+ from typing import TYPE_CHECKING, Any, Iterator
3
5
 
4
6
  from .connectors import get_all_connectors, get_execution_connectors
5
7
  from .exceptions import NoConnectorError, NoGroupError, NoHostError
@@ -9,7 +11,7 @@ if TYPE_CHECKING:
9
11
  from pyinfra.api.state import State
10
12
 
11
13
 
12
- def extract_name_data(names: List[Any]):
14
+ def extract_name_data(names: list[Any]):
13
15
  for name in names:
14
16
  data = {}
15
17
 
@@ -38,8 +40,8 @@ class Inventory:
38
40
  def __init__(self, names_data, override_data=None, **groups):
39
41
  # Setup basics
40
42
  self.groups = defaultdict(list) # lists of Host objects
41
- self.host_data = defaultdict(dict) # dict of name -> data
42
- self.group_data = defaultdict(dict) # dict of name -> data
43
+ self.host_data: dict[str, dict] = defaultdict(dict) # dict of name -> data
44
+ self.group_data: dict[str, dict] = defaultdict(dict) # dict of name -> data
43
45
  self.override_data = override_data or {}
44
46
 
45
47
  names, data = names_data
@@ -48,14 +50,14 @@ class Inventory:
48
50
  self.data = data
49
51
 
50
52
  # Create the actual host instances and groups
51
- self.hosts = self.make_hosts_and_groups(names, groups)
53
+ self.make_hosts_and_groups(names, groups)
52
54
 
53
- def make_hosts_and_groups(self, names, groups):
55
+ def make_hosts_and_groups(self, names, groups) -> None:
54
56
  all_connectors = get_all_connectors()
55
57
  execution_connectors = get_execution_connectors()
56
58
 
57
59
  # Map name -> data
58
- name_to_data = defaultdict(dict)
60
+ name_to_data: dict[str, dict] = defaultdict(dict)
59
61
  # Map name -> group names
60
62
  name_to_group_names = defaultdict(list)
61
63
 
@@ -73,14 +75,14 @@ class Inventory:
73
75
  for name, data in extract_name_data(names):
74
76
  name_to_data[name].update(data)
75
77
 
76
- # Now, use the above to fill self.host_data and populate names_executors
77
- names_executors = []
78
+ # Now, use the above to fill self.host_data and populate names_connectors
79
+ names_connectors = []
78
80
 
79
81
  for name, _ in extract_name_data(names):
80
82
  host_data = name_to_data[name]
81
83
 
82
84
  # Default to executing commands with the ssh connector
83
- executor = execution_connectors["ssh"]
85
+ connector_cls = execution_connectors["ssh"]
84
86
 
85
87
  if name[0] == "@":
86
88
  connector_name = name[1:]
@@ -96,7 +98,7 @@ class Inventory:
96
98
 
97
99
  # Execution connector? Simple, just set it for their host
98
100
  if connector_name in execution_connectors:
99
- executor = execution_connectors[connector_name]
101
+ connector_cls = execution_connectors[connector_name]
100
102
 
101
103
  names_data = all_connectors[connector_name].make_names_data(arg_string)
102
104
  connector_inventory_name = name
@@ -111,7 +113,7 @@ class Inventory:
111
113
 
112
114
  # Assign the name/data/groups from the connector
113
115
  self.host_data[sub_name] = sub_data
114
- names_executors.append((sub_name, executor))
116
+ names_connectors.append((sub_name, connector_cls))
115
117
  name_to_group_names[sub_name].extend(sub_groups)
116
118
 
117
119
  # If we have a connector inventory name, copy any groups attached
@@ -124,10 +126,10 @@ class Inventory:
124
126
  # Now we can actually make Host instances
125
127
  hosts: dict[str, "Host"] = {}
126
128
 
127
- for name, executor in names_executors:
129
+ for name, connector_cls in names_connectors:
128
130
  host_groups = name_to_group_names[name]
129
131
 
130
- host = Host(name, inventory=self, groups=host_groups, executor=executor)
132
+ host = Host(name, inventory=self, groups=host_groups, connector_cls=connector_cls)
131
133
  hosts[name] = host
132
134
 
133
135
  # And push into any groups
@@ -135,7 +137,7 @@ class Inventory:
135
137
  if host not in self.groups[group_name]:
136
138
  self.groups[group_name].append(host)
137
139
 
138
- return hosts
140
+ self.hosts = hosts
139
141
 
140
142
  def __len__(self) -> int:
141
143
  """
@@ -241,13 +243,3 @@ class Inventory:
241
243
  data.update(self.get_group_data(group))
242
244
 
243
245
  return data
244
-
245
- def get_deploy_data(self):
246
- """
247
- Gets any default data attached to the current deploy, if any.
248
- """
249
-
250
- if self.state and self.state.deploy_data:
251
- return self.state.deploy_data
252
-
253
- return {}