pyinfra 0.11.dev3__py3-none-any.whl → 3.6__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 (204) hide show
  1. pyinfra/__init__.py +9 -12
  2. pyinfra/__main__.py +4 -0
  3. pyinfra/api/__init__.py +19 -3
  4. pyinfra/api/arguments.py +413 -0
  5. pyinfra/api/arguments_typed.py +79 -0
  6. pyinfra/api/command.py +274 -0
  7. pyinfra/api/config.py +222 -28
  8. pyinfra/api/connect.py +33 -13
  9. pyinfra/api/connectors.py +27 -0
  10. pyinfra/api/deploy.py +65 -66
  11. pyinfra/api/exceptions.py +73 -18
  12. pyinfra/api/facts.py +267 -200
  13. pyinfra/api/host.py +416 -50
  14. pyinfra/api/inventory.py +121 -160
  15. pyinfra/api/metadata.py +69 -0
  16. pyinfra/api/operation.py +432 -262
  17. pyinfra/api/operations.py +273 -260
  18. pyinfra/api/state.py +302 -248
  19. pyinfra/api/util.py +309 -369
  20. pyinfra/connectors/base.py +173 -0
  21. pyinfra/connectors/chroot.py +212 -0
  22. pyinfra/connectors/docker.py +405 -0
  23. pyinfra/connectors/dockerssh.py +297 -0
  24. pyinfra/connectors/local.py +238 -0
  25. pyinfra/connectors/scp/__init__.py +1 -0
  26. pyinfra/connectors/scp/client.py +204 -0
  27. pyinfra/connectors/ssh.py +727 -0
  28. pyinfra/connectors/ssh_util.py +114 -0
  29. pyinfra/connectors/sshuserclient/client.py +309 -0
  30. pyinfra/connectors/sshuserclient/config.py +102 -0
  31. pyinfra/connectors/terraform.py +135 -0
  32. pyinfra/connectors/util.py +417 -0
  33. pyinfra/connectors/vagrant.py +183 -0
  34. pyinfra/context.py +145 -0
  35. pyinfra/facts/__init__.py +7 -6
  36. pyinfra/facts/apk.py +22 -7
  37. pyinfra/facts/apt.py +117 -60
  38. pyinfra/facts/brew.py +100 -15
  39. pyinfra/facts/bsdinit.py +23 -0
  40. pyinfra/facts/cargo.py +37 -0
  41. pyinfra/facts/choco.py +47 -0
  42. pyinfra/facts/crontab.py +195 -0
  43. pyinfra/facts/deb.py +94 -0
  44. pyinfra/facts/dnf.py +48 -0
  45. pyinfra/facts/docker.py +96 -23
  46. pyinfra/facts/efibootmgr.py +113 -0
  47. pyinfra/facts/files.py +629 -58
  48. pyinfra/facts/flatpak.py +77 -0
  49. pyinfra/facts/freebsd.py +70 -0
  50. pyinfra/facts/gem.py +19 -6
  51. pyinfra/facts/git.py +59 -14
  52. pyinfra/facts/gpg.py +150 -0
  53. pyinfra/facts/hardware.py +313 -167
  54. pyinfra/facts/iptables.py +72 -62
  55. pyinfra/facts/launchd.py +44 -0
  56. pyinfra/facts/lxd.py +17 -4
  57. pyinfra/facts/mysql.py +122 -86
  58. pyinfra/facts/npm.py +17 -9
  59. pyinfra/facts/openrc.py +71 -0
  60. pyinfra/facts/opkg.py +246 -0
  61. pyinfra/facts/pacman.py +50 -7
  62. pyinfra/facts/pip.py +24 -7
  63. pyinfra/facts/pipx.py +82 -0
  64. pyinfra/facts/pkg.py +15 -6
  65. pyinfra/facts/pkgin.py +35 -0
  66. pyinfra/facts/podman.py +54 -0
  67. pyinfra/facts/postgres.py +178 -0
  68. pyinfra/facts/postgresql.py +6 -147
  69. pyinfra/facts/rpm.py +105 -0
  70. pyinfra/facts/runit.py +77 -0
  71. pyinfra/facts/selinux.py +161 -0
  72. pyinfra/facts/server.py +762 -285
  73. pyinfra/facts/snap.py +88 -0
  74. pyinfra/facts/systemd.py +139 -0
  75. pyinfra/facts/sysvinit.py +59 -0
  76. pyinfra/facts/upstart.py +35 -0
  77. pyinfra/facts/util/__init__.py +17 -0
  78. pyinfra/facts/util/databases.py +4 -6
  79. pyinfra/facts/util/packaging.py +37 -6
  80. pyinfra/facts/util/units.py +30 -0
  81. pyinfra/facts/util/win_files.py +99 -0
  82. pyinfra/facts/vzctl.py +20 -13
  83. pyinfra/facts/xbps.py +35 -0
  84. pyinfra/facts/yum.py +34 -40
  85. pyinfra/facts/zfs.py +77 -0
  86. pyinfra/facts/zypper.py +42 -0
  87. pyinfra/local.py +45 -83
  88. pyinfra/operations/__init__.py +12 -0
  89. pyinfra/operations/apk.py +99 -0
  90. pyinfra/operations/apt.py +496 -0
  91. pyinfra/operations/brew.py +232 -0
  92. pyinfra/operations/bsdinit.py +59 -0
  93. pyinfra/operations/cargo.py +45 -0
  94. pyinfra/operations/choco.py +61 -0
  95. pyinfra/operations/crontab.py +194 -0
  96. pyinfra/operations/dnf.py +213 -0
  97. pyinfra/operations/docker.py +492 -0
  98. pyinfra/operations/files.py +2014 -0
  99. pyinfra/operations/flatpak.py +95 -0
  100. pyinfra/operations/freebsd/__init__.py +12 -0
  101. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  102. pyinfra/operations/freebsd/pkg.py +219 -0
  103. pyinfra/operations/freebsd/service.py +116 -0
  104. pyinfra/operations/freebsd/sysrc.py +92 -0
  105. pyinfra/operations/gem.py +48 -0
  106. pyinfra/operations/git.py +420 -0
  107. pyinfra/operations/iptables.py +312 -0
  108. pyinfra/operations/launchd.py +45 -0
  109. pyinfra/operations/lxd.py +69 -0
  110. pyinfra/operations/mysql.py +610 -0
  111. pyinfra/operations/npm.py +57 -0
  112. pyinfra/operations/openrc.py +63 -0
  113. pyinfra/operations/opkg.py +89 -0
  114. pyinfra/operations/pacman.py +82 -0
  115. pyinfra/operations/pip.py +206 -0
  116. pyinfra/operations/pipx.py +103 -0
  117. pyinfra/operations/pkg.py +71 -0
  118. pyinfra/operations/pkgin.py +92 -0
  119. pyinfra/operations/postgres.py +437 -0
  120. pyinfra/operations/postgresql.py +30 -0
  121. pyinfra/operations/puppet.py +41 -0
  122. pyinfra/operations/python.py +73 -0
  123. pyinfra/operations/runit.py +184 -0
  124. pyinfra/operations/selinux.py +190 -0
  125. pyinfra/operations/server.py +1100 -0
  126. pyinfra/operations/snap.py +118 -0
  127. pyinfra/operations/ssh.py +217 -0
  128. pyinfra/operations/systemd.py +150 -0
  129. pyinfra/operations/sysvinit.py +142 -0
  130. pyinfra/operations/upstart.py +68 -0
  131. pyinfra/operations/util/__init__.py +12 -0
  132. pyinfra/operations/util/docker.py +407 -0
  133. pyinfra/operations/util/files.py +247 -0
  134. pyinfra/operations/util/packaging.py +338 -0
  135. pyinfra/operations/util/service.py +46 -0
  136. pyinfra/operations/vzctl.py +137 -0
  137. pyinfra/operations/xbps.py +78 -0
  138. pyinfra/operations/yum.py +213 -0
  139. pyinfra/operations/zfs.py +176 -0
  140. pyinfra/operations/zypper.py +193 -0
  141. pyinfra/progress.py +44 -32
  142. pyinfra/py.typed +0 -0
  143. pyinfra/version.py +9 -1
  144. pyinfra-3.6.dist-info/METADATA +142 -0
  145. pyinfra-3.6.dist-info/RECORD +160 -0
  146. {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info}/WHEEL +1 -2
  147. pyinfra-3.6.dist-info/entry_points.txt +12 -0
  148. {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info/licenses}/LICENSE.md +1 -1
  149. pyinfra_cli/__init__.py +1 -0
  150. pyinfra_cli/cli.py +793 -0
  151. pyinfra_cli/commands.py +66 -0
  152. pyinfra_cli/exceptions.py +155 -65
  153. pyinfra_cli/inventory.py +233 -89
  154. pyinfra_cli/log.py +39 -43
  155. pyinfra_cli/main.py +26 -495
  156. pyinfra_cli/prints.py +215 -156
  157. pyinfra_cli/util.py +172 -105
  158. pyinfra_cli/virtualenv.py +25 -20
  159. pyinfra/api/connectors/__init__.py +0 -21
  160. pyinfra/api/connectors/ansible.py +0 -99
  161. pyinfra/api/connectors/docker.py +0 -178
  162. pyinfra/api/connectors/local.py +0 -169
  163. pyinfra/api/connectors/ssh.py +0 -402
  164. pyinfra/api/connectors/sshuserclient/client.py +0 -105
  165. pyinfra/api/connectors/sshuserclient/config.py +0 -90
  166. pyinfra/api/connectors/util.py +0 -63
  167. pyinfra/api/connectors/vagrant.py +0 -155
  168. pyinfra/facts/init.py +0 -176
  169. pyinfra/facts/util/files.py +0 -102
  170. pyinfra/hook.py +0 -41
  171. pyinfra/modules/__init__.py +0 -11
  172. pyinfra/modules/apk.py +0 -64
  173. pyinfra/modules/apt.py +0 -272
  174. pyinfra/modules/brew.py +0 -122
  175. pyinfra/modules/files.py +0 -711
  176. pyinfra/modules/gem.py +0 -30
  177. pyinfra/modules/git.py +0 -115
  178. pyinfra/modules/init.py +0 -344
  179. pyinfra/modules/iptables.py +0 -271
  180. pyinfra/modules/lxd.py +0 -45
  181. pyinfra/modules/mysql.py +0 -347
  182. pyinfra/modules/npm.py +0 -47
  183. pyinfra/modules/pacman.py +0 -60
  184. pyinfra/modules/pip.py +0 -99
  185. pyinfra/modules/pkg.py +0 -43
  186. pyinfra/modules/postgresql.py +0 -245
  187. pyinfra/modules/puppet.py +0 -20
  188. pyinfra/modules/python.py +0 -37
  189. pyinfra/modules/server.py +0 -524
  190. pyinfra/modules/ssh.py +0 -150
  191. pyinfra/modules/util/files.py +0 -52
  192. pyinfra/modules/util/packaging.py +0 -118
  193. pyinfra/modules/vzctl.py +0 -133
  194. pyinfra/modules/yum.py +0 -171
  195. pyinfra/pseudo_modules.py +0 -64
  196. pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
  197. pyinfra-0.11.dev3.dist-info/METADATA +0 -135
  198. pyinfra-0.11.dev3.dist-info/RECORD +0 -95
  199. pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
  200. pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
  201. pyinfra_cli/__main__.py +0 -40
  202. pyinfra_cli/config.py +0 -92
  203. /pyinfra/{modules/util → connectors}/__init__.py +0 -0
  204. /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
pyinfra_cli/util.py CHANGED
@@ -1,98 +1,143 @@
1
- import json
1
+ from __future__ import annotations
2
2
 
3
+ import json
4
+ import os
3
5
  from datetime import datetime
4
6
  from importlib import import_module
7
+ from importlib.util import find_spec
5
8
  from io import IOBase
6
- from types import FunctionType
9
+ from os import path
10
+ from pathlib import Path
11
+ from types import CodeType, FunctionType, ModuleType
12
+ from typing import Callable
7
13
 
8
14
  import click
15
+ import gevent
16
+
17
+ from pyinfra import logger, state
18
+ from pyinfra.api.command import PyinfraCommand
19
+ from pyinfra.api.exceptions import PyinfraError
20
+ from pyinfra.api.host import HostData
21
+ from pyinfra.api.operation import OperationMeta
22
+ from pyinfra.api.state import (
23
+ State,
24
+ StateHostMeta,
25
+ StateHostResults,
26
+ StateOperationHostData,
27
+ StateOperationMeta,
28
+ )
29
+ from pyinfra.context import ctx_config, ctx_host
30
+ from pyinfra.progress import progress_spinner
31
+
32
+ from .exceptions import CliError, UnexpectedExternalError
9
33
 
10
- from pyinfra import logger, pseudo_host, pseudo_state
11
- from pyinfra.api.util import FallbackDict
12
- from pyinfra.hook import HOOKS
34
+ # Cache for compiled Python deploy code
35
+ PYTHON_CODES: dict[str, CodeType] = {}
13
36
 
14
- from .exceptions import CliError
15
37
 
16
- # Cache for compiled Python deploy code
17
- PYTHON_CODES = {}
38
+ def is_subdir(child, parent):
39
+ child = path.realpath(child)
40
+ parent = path.realpath(parent)
41
+ relative = path.relpath(child, start=parent)
42
+ return not relative.startswith(os.pardir)
18
43
 
19
44
 
20
- def exec_file(filename, return_locals=False, is_deploy_code=False):
21
- '''
45
+ def exec_file(filename, return_locals: bool = False, is_deploy_code: bool = False):
46
+ """
22
47
  Execute a Python file and optionally return it's attributes as a dict.
23
- '''
48
+ """
49
+
50
+ old_current_exec_filename = state.current_exec_filename
51
+ state.current_exec_filename = filename
24
52
 
25
53
  if filename not in PYTHON_CODES:
26
- with open(filename, 'r') as f:
27
- code = f.read()
54
+ with open(filename, "r", encoding="utf-8") as f:
55
+ code_str = f.read()
28
56
 
29
- code = compile(code, filename, 'exec')
57
+ code = compile(code_str, filename, "exec")
30
58
  PYTHON_CODES[filename] = code
31
59
 
32
60
  # Create some base attributes for our "module"
33
- data = {
34
- '__file__': filename,
35
- 'state': pseudo_state,
36
- }
61
+ data = {"__file__": filename}
37
62
 
38
63
  # Execute the code with locals/globals going into the dict above
39
- exec(PYTHON_CODES[filename], data)
64
+ try:
65
+ exec(PYTHON_CODES[filename], data)
66
+ except PyinfraError:
67
+ # Raise pyinfra errors as-is
68
+ raise
69
+ except Exception as e:
70
+ # Wrap & re-raise errors in user code so we highlight filename/etc
71
+ raise UnexpectedExternalError(e, filename)
72
+ finally:
73
+ state.current_exec_filename = old_current_exec_filename
40
74
 
41
75
  return data
42
76
 
43
77
 
44
- def run_hook(state, hook_name, hook_data):
45
- hooks = HOOKS[hook_name]
46
-
47
- if hooks:
48
- for hook in hooks:
49
- print('--> Running hook: {0}/{1}'.format(
50
- hook_name,
51
- click.style(hook.__name__, bold=True),
52
- ))
53
- hook(hook_data, state)
54
-
55
- print()
78
+ def json_encode(obj):
79
+ # pyinfra types
80
+ if isinstance(obj, HostData):
81
+ return obj.dict()
56
82
 
83
+ if isinstance(obj, PyinfraCommand):
84
+ return repr(obj)
85
+
86
+ if isinstance(
87
+ obj,
88
+ (
89
+ OperationMeta,
90
+ StateOperationMeta,
91
+ StateOperationHostData,
92
+ StateHostMeta,
93
+ StateHostResults,
94
+ ),
95
+ ):
96
+ return repr(obj)
97
+
98
+ # Python types
99
+ if isinstance(obj, ModuleType):
100
+ return "Module: {0}".format(obj.__name__)
57
101
 
58
- def json_encode(obj):
59
102
  if isinstance(obj, FunctionType):
60
- return obj.__name__
103
+ return "Function: {0}".format(obj.__name__)
61
104
 
62
- elif isinstance(obj, datetime):
105
+ if isinstance(obj, datetime):
63
106
  return obj.isoformat()
64
107
 
65
- elif isinstance(obj, FallbackDict):
66
- return obj.dict()
67
-
68
- elif isinstance(obj, IOBase):
69
- if hasattr(obj, 'name'):
70
- return 'File: {0}'.format(obj.name)
108
+ if isinstance(obj, IOBase):
109
+ if hasattr(obj, "name"):
110
+ return "File: {0}".format(obj.name)
71
111
 
72
- elif hasattr(obj, 'template'):
73
- return 'Template: {0}'.format(obj.template)
112
+ if hasattr(obj, "template"):
113
+ return "Template: {0}".format(obj.template)
74
114
 
75
115
  obj.seek(0)
76
- return 'In memory file: {0}'.format(obj.read())
116
+ return "In memory file: {0}".format(obj.read())
117
+
118
+ if isinstance(obj, Path):
119
+ return str(obj)
77
120
 
78
- elif isinstance(obj, set):
121
+ if isinstance(obj, set):
79
122
  return sorted(list(obj))
80
123
 
81
- elif isinstance(obj, bytes):
124
+ if isinstance(obj, bytes):
82
125
  return obj.decode()
83
126
 
84
- else:
85
- raise TypeError('Cannot serialize: {0} ({1})'.format(type(obj), obj))
127
+ if hasattr(obj, "to_json"):
128
+ return obj.to_json()
129
+
130
+ raise TypeError("Cannot serialize: {0} ({1})".format(type(obj), obj))
86
131
 
87
132
 
88
- def _parse_arg(arg):
133
+ def parse_cli_arg(arg):
89
134
  if isinstance(arg, list):
90
- return [_parse_arg(a) for a in arg]
135
+ return [parse_cli_arg(a) for a in arg]
91
136
 
92
- if arg.lower() == 'false':
137
+ if arg.lower() == "false":
93
138
  return False
94
139
 
95
- if arg.lower() == 'true':
140
+ if arg.lower() == "true":
96
141
  return True
97
142
 
98
143
  try:
@@ -100,73 +145,95 @@ def _parse_arg(arg):
100
145
  except (TypeError, ValueError):
101
146
  pass
102
147
 
148
+ try:
149
+ return json.loads(arg)
150
+ except ValueError:
151
+ pass
152
+
103
153
  return arg
104
154
 
105
155
 
106
- def get_operation_and_args(commands):
107
- operation_name = commands[0]
156
+ def try_import_module_attribute(path, prefix=None, raise_for_none=True):
157
+ if ":" in path:
158
+ # Allow a.module.name:function syntax
159
+ mod_path, attr_name = path.rsplit(":", 1)
160
+ elif "." in path:
161
+ # And also a.module.name.function
162
+ mod_path, attr_name = path.rsplit(".", 1)
163
+ else:
164
+ return None
108
165
 
109
- # Get the module & operation name
110
- op_module, op_name = operation_name.split('.')
166
+ possible_modules = [mod_path]
167
+ if prefix:
168
+ possible_modules.append(f"{prefix}.{mod_path}")
111
169
 
112
- try:
113
- op_module = import_module('pyinfra.modules.{0}'.format(op_module))
114
- except ImportError:
115
- raise CliError('No such module: {0}'.format(op_module))
170
+ module = None
116
171
 
117
- op = getattr(op_module, op_name, None)
118
- if not op:
119
- raise CliError('No such operation: {0}'.format(operation_name))
172
+ for possible in possible_modules:
173
+ try:
174
+ # Look for the module/fn, note that from the find_spec doc:
175
+ # "If the name is for submodule (contains a dot), the parent module is
176
+ # automatically imported."
177
+ spec = find_spec(possible)
178
+ except ModuleNotFoundError:
179
+ continue
180
+ except Exception as e:
181
+ # Capture all exceptions here which may be triggered from the automatic module import
182
+ # referenced above the find_spec call.
183
+ logger.warning(f"Exception raised during inventory search on: {possible}: {e}")
184
+ continue
185
+ else:
186
+ if spec is not None:
187
+ module = import_module(possible)
188
+ break
120
189
 
121
- # Parse the arguments
122
- operation_args = commands[1:]
190
+ if module is None:
191
+ if raise_for_none:
192
+ raise CliError(f"No such module: {possible_modules[0]}")
193
+ return
123
194
 
124
- if len(operation_args) == 1:
125
- # Check if we're JSON (in which case we expect a list of two items:
126
- # a list of args and a dict of kwargs).
127
- try:
128
- args, kwargs = json.loads(operation_args[0])
129
- return op, (args, kwargs)
130
- except ValueError:
131
- pass
132
-
133
- args = [
134
- _parse_arg(arg)
135
- for arg in operation_args if '=' not in arg
136
- ]
137
-
138
- kwargs = {
139
- key: _parse_arg(value)
140
- for key, value in [
141
- arg.split('=', 1)
142
- for arg in operation_args if '=' in arg
143
- ]
144
- }
195
+ attr = getattr(module, attr_name, None)
196
+ if attr is None:
197
+ if raise_for_none:
198
+ raise CliError(f"No such attribute in module {possible_modules[0]}: {attr_name}")
199
+ return
145
200
 
146
- return op, (args, kwargs)
201
+ return attr
147
202
 
148
203
 
149
- def load_deploy_file(state, filename):
150
- # Copy the inventory hosts (some might be removed during deploy)
151
- hosts = list(state.inventory)
204
+ def _parallel_load_hosts(state: "State", callback: Callable, name: str):
205
+ def load_file(local_host):
206
+ try:
207
+ with ctx_config.use(state.config.copy()):
208
+ with ctx_host.use(local_host):
209
+ callback()
210
+ logger.info(
211
+ "{0}{1} {2}".format(
212
+ local_host.print_prefix,
213
+ click.style("Ready:", "green"),
214
+ click.style(name, bold=True),
215
+ ),
216
+ )
217
+ except Exception as e:
218
+ return e
219
+
220
+ greenlet_to_host = {
221
+ state.pool.spawn(load_file, host): host for host in state.inventory.iter_active_hosts()
222
+ }
152
223
 
153
- for host in hosts:
154
- # Don't load for anything within our (top level, --limit) limit
155
- if (
156
- isinstance(state.limit_hosts, list)
157
- and host not in state.limit_hosts
158
- ):
159
- continue
224
+ with progress_spinner(greenlet_to_host.values()) as progress:
225
+ for greenlet in gevent.iwait(greenlet_to_host.keys()):
226
+ host = greenlet_to_host[greenlet]
227
+ result = greenlet.get()
228
+ if isinstance(result, Exception):
229
+ raise result
230
+ progress(host)
160
231
 
161
- pseudo_host.set(host)
162
232
 
163
- exec_file(filename)
233
+ def load_deploy_file(state: "State", filename):
234
+ state.current_deploy_filename = filename
235
+ _parallel_load_hosts(state, lambda: exec_file(filename), filename)
164
236
 
165
- logger.info('{0} {1} {2}'.format(
166
- host.print_prefix,
167
- click.style('Ready:', 'green'),
168
- click.style(filename, bold=True),
169
- ))
170
237
 
171
- # Remove any pseudo host
172
- pseudo_host.reset()
238
+ def load_func(state: "State", op_func, *args, **kwargs):
239
+ _parallel_load_hosts(state, lambda: op_func(*args, **kwargs), op_func.__name__)
pyinfra_cli/virtualenv.py CHANGED
@@ -1,11 +1,13 @@
1
1
  import os
2
2
  import sys
3
3
 
4
+ import click
5
+
4
6
  from pyinfra import logger
5
7
 
6
8
 
7
- def init_virtualenv():
8
- '''
9
+ def init_virtualenv() -> None:
10
+ """
9
11
  Add a virtualenv to sys.path so the user can import modules from it.
10
12
  This isn't perfect: it doesn't use the Python interpreter with which the
11
13
  virtualenv was built, and it ignores the --no-site-packages option. A
@@ -14,14 +16,14 @@ def init_virtualenv():
14
16
 
15
17
  Adapted from IPython's implementation:
16
18
  https://github.com/ipython/ipython/blob/master/IPython/core/interactiveshell.py
17
- '''
19
+ """
18
20
 
19
- if 'VIRTUAL_ENV' not in os.environ:
21
+ if "VIRTUAL_ENV" not in os.environ:
20
22
  # Not in a virtualenv
21
23
  return
22
24
 
23
25
  p = os.path.normcase(sys.executable)
24
- p_venv = os.path.normcase(os.environ['VIRTUAL_ENV'])
26
+ p_venv = os.path.normcase(os.environ["VIRTUAL_ENV"])
25
27
 
26
28
  # executable path should end like /bin/python or \\scripts\\python.exe
27
29
  p_exe_up2 = os.path.dirname(os.path.dirname(p))
@@ -39,35 +41,38 @@ def init_virtualenv():
39
41
  paths.append(p)
40
42
 
41
43
  # In Cygwin paths like 'c:\...' and '\cygdrive\c\...' are possible
42
- if p_venv.startswith('\\cygdrive'):
44
+ if p_venv.startswith("\\cygdrive"):
43
45
  p_venv = p_venv[11:]
44
- elif len(p_venv) >= 2 and p_venv[1] == ':':
46
+ elif len(p_venv) >= 2 and p_venv[1] == ":":
45
47
  p_venv = p_venv[2:]
46
48
 
47
49
  if any(p_venv in p for p in paths):
48
50
  # Running properly in the virtualenv, don't need to do anything
49
51
  return
50
52
 
51
- logger.warning((
52
- 'Attempting to work in a virtualenv.\n'
53
- ' If you encounter problems, please install pyinfra inside the virtualenv.'
54
- ))
55
- print()
53
+ logger.warning(
54
+ (
55
+ "Attempting to work in a virtualenv.\n"
56
+ " If you encounter problems, please install pyinfra inside the virtualenv."
57
+ ),
58
+ )
59
+ click.echo(err=True)
56
60
 
57
- if sys.platform == 'win32':
61
+ if sys.platform == "win32":
58
62
  virtual_env = os.path.join(
59
- os.environ['VIRTUAL_ENV'],
60
- 'Lib',
61
- 'site-packages',
63
+ os.environ["VIRTUAL_ENV"],
64
+ "Lib",
65
+ "site-packages",
62
66
  )
63
67
  else:
64
68
  virtual_env = os.path.join(
65
- os.environ['VIRTUAL_ENV'],
66
- 'lib',
67
- 'python%d.%d' % sys.version_info[:2],
68
- 'site-packages',
69
+ os.environ["VIRTUAL_ENV"],
70
+ "lib",
71
+ "python%d.%d" % sys.version_info[:2],
72
+ "site-packages",
69
73
  )
70
74
 
71
75
  import site
76
+
72
77
  sys.path.insert(0, virtual_env)
73
78
  site.addsitedir(virtual_env)
@@ -1,21 +0,0 @@
1
- from . import ansible, docker, local, ssh, vagrant
2
-
3
-
4
- # Connectors that handle execution of pyinfra operations
5
- EXECUTION_CONNECTORS = { # pragma: no cover
6
- 'docker': docker,
7
- 'local': local,
8
- 'ssh': ssh,
9
- }
10
-
11
- # Connectors that handle generation of inventories
12
- INVENTORY_CONNECTORS = { # pragma: no cover
13
- 'docker': docker,
14
- 'vagrant': vagrant,
15
- 'ansible': ansible,
16
- }
17
-
18
- ALL_CONNECTORS = ( # pragma: no cover
19
- list(EXECUTION_CONNECTORS.keys())
20
- + list(INVENTORY_CONNECTORS.keys())
21
- )
@@ -1,99 +0,0 @@
1
- import re
2
-
3
- from collections import defaultdict
4
- from configparser import ConfigParser
5
- from os import path
6
-
7
- from pyinfra import logger
8
- from pyinfra.api.exceptions import InventoryError
9
- from pyinfra.api.util import memoize
10
-
11
-
12
- @memoize
13
- def show_warning():
14
- logger.warning('The @ansible connector is in Alpha!')
15
-
16
-
17
- @memoize
18
- def get_ansible_inventory(inventory_filename=None):
19
- if not inventory_filename: # pragma: no cover
20
- raise InventoryError('No Ansible inventory filename provided!')
21
-
22
- if not path.exists(inventory_filename):
23
- raise InventoryError((
24
- 'Could not find Ansible inventory file: {0}'
25
- ).format(inventory_filename))
26
-
27
- logger.info('Parsing Ansible inventory...')
28
-
29
- config = ConfigParser(
30
- delimiters=(' '), # we only handle the hostnames for now
31
- allow_no_value=True, # we don't by default have = values
32
- interpolation=None, # remove any Python interpolation
33
- )
34
- config.read(inventory_filename)
35
- return config
36
-
37
-
38
- def _parse_ansible_hosts(hosts):
39
- for host in hosts:
40
- expand_match = re.search(r'\[[0-9:]+\]', host)
41
- if expand_match:
42
- expand_string = host[expand_match.start():expand_match.end()]
43
- bits = expand_string[1:-1].split(':') # remove the [] either side
44
-
45
- zfill = 0
46
- if bits[0].startswith('0'):
47
- zfill = len(bits[0])
48
-
49
- start, end = int(bits[0]), int(bits[1])
50
- step = int(bits[2]) if len(bits) > 2 else 1
51
-
52
- for n in range(start, end + 1, step):
53
- number_as_string = '{0}'.format(n)
54
- if zfill:
55
- number_as_string = number_as_string.zfill(zfill)
56
-
57
- hostname = host.replace(expand_string, number_as_string)
58
- yield hostname
59
- else:
60
- yield host
61
-
62
-
63
- def make_names_data(inventory_filename=None):
64
- show_warning()
65
-
66
- config = get_ansible_inventory(inventory_filename)
67
-
68
- host_to_groups = defaultdict(set)
69
- group_to_hosts = defaultdict(set)
70
- hosts = []
71
-
72
- # First pass - load hosts/groups of hosts
73
- for section in config.sections():
74
- if ':' in section: # ignore :children and :vars sections this time
75
- continue
76
-
77
- options = config.options(section)
78
- for host in _parse_ansible_hosts(options):
79
- hosts.append(host)
80
- host_to_groups[host].add(section)
81
- group_to_hosts[section].add(host)
82
-
83
- # Second pass - load any children groups
84
- for section in config.sections():
85
- if not section.endswith(':children'): # we only support :children for now
86
- continue
87
-
88
- group_name = section.replace(':children', '')
89
-
90
- options = config.options(section)
91
- for sub_group_name in options:
92
- sub_group_hosts = group_to_hosts[sub_group_name]
93
- for host in sub_group_hosts:
94
- host_to_groups[host].add(group_name)
95
-
96
- return [
97
- (host, {}, sorted(list(host_to_groups.get(host))))
98
- for host in hosts
99
- ]