pyinfra 3.0.dev0__py2.py3-none-any.whl → 3.0.1__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 (148) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +115 -97
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +5 -3
  5. pyinfra/api/config.py +139 -39
  6. pyinfra/api/connectors.py +5 -2
  7. pyinfra/api/deploy.py +19 -19
  8. pyinfra/api/exceptions.py +35 -4
  9. pyinfra/api/facts.py +62 -86
  10. pyinfra/api/host.py +102 -15
  11. pyinfra/api/inventory.py +4 -0
  12. pyinfra/api/operation.py +184 -118
  13. pyinfra/api/operations.py +66 -113
  14. pyinfra/api/state.py +53 -34
  15. pyinfra/api/util.py +64 -33
  16. pyinfra/connectors/base.py +65 -20
  17. pyinfra/connectors/chroot.py +15 -13
  18. pyinfra/connectors/docker.py +62 -72
  19. pyinfra/connectors/dockerssh.py +20 -19
  20. pyinfra/connectors/local.py +32 -22
  21. pyinfra/connectors/ssh.py +162 -86
  22. pyinfra/connectors/sshuserclient/client.py +1 -1
  23. pyinfra/connectors/terraform.py +57 -39
  24. pyinfra/connectors/util.py +26 -27
  25. pyinfra/connectors/vagrant.py +27 -26
  26. pyinfra/context.py +1 -0
  27. pyinfra/facts/apk.py +7 -2
  28. pyinfra/facts/apt.py +15 -7
  29. pyinfra/facts/brew.py +28 -13
  30. pyinfra/facts/bsdinit.py +9 -6
  31. pyinfra/facts/cargo.py +6 -3
  32. pyinfra/facts/choco.py +8 -4
  33. pyinfra/facts/deb.py +21 -9
  34. pyinfra/facts/dnf.py +11 -6
  35. pyinfra/facts/docker.py +30 -5
  36. pyinfra/facts/files.py +49 -33
  37. pyinfra/facts/gem.py +7 -2
  38. pyinfra/facts/git.py +14 -21
  39. pyinfra/facts/gpg.py +4 -1
  40. pyinfra/facts/hardware.py +186 -138
  41. pyinfra/facts/launchd.py +7 -2
  42. pyinfra/facts/lxd.py +8 -2
  43. pyinfra/facts/mysql.py +19 -12
  44. pyinfra/facts/npm.py +3 -1
  45. pyinfra/facts/openrc.py +8 -2
  46. pyinfra/facts/pacman.py +13 -5
  47. pyinfra/facts/pip.py +2 -0
  48. pyinfra/facts/pkg.py +5 -1
  49. pyinfra/facts/pkgin.py +7 -2
  50. pyinfra/facts/postgres.py +170 -0
  51. pyinfra/facts/postgresql.py +5 -162
  52. pyinfra/facts/rpm.py +21 -15
  53. pyinfra/facts/runit.py +70 -0
  54. pyinfra/facts/selinux.py +12 -4
  55. pyinfra/facts/server.py +240 -82
  56. pyinfra/facts/snap.py +8 -2
  57. pyinfra/facts/systemd.py +37 -13
  58. pyinfra/facts/sysvinit.py +7 -4
  59. pyinfra/facts/upstart.py +7 -2
  60. pyinfra/facts/util/packaging.py +3 -2
  61. pyinfra/facts/vzctl.py +8 -4
  62. pyinfra/facts/xbps.py +7 -2
  63. pyinfra/facts/yum.py +10 -5
  64. pyinfra/facts/zypper.py +9 -4
  65. pyinfra/operations/apk.py +5 -3
  66. pyinfra/operations/apt.py +28 -25
  67. pyinfra/operations/brew.py +60 -29
  68. pyinfra/operations/bsdinit.py +6 -4
  69. pyinfra/operations/cargo.py +3 -1
  70. pyinfra/operations/choco.py +3 -1
  71. pyinfra/operations/dnf.py +16 -20
  72. pyinfra/operations/docker.py +339 -0
  73. pyinfra/operations/files.py +187 -168
  74. pyinfra/operations/gem.py +3 -1
  75. pyinfra/operations/git.py +23 -25
  76. pyinfra/operations/iptables.py +33 -25
  77. pyinfra/operations/launchd.py +5 -6
  78. pyinfra/operations/lxd.py +7 -4
  79. pyinfra/operations/mysql.py +59 -55
  80. pyinfra/operations/npm.py +8 -1
  81. pyinfra/operations/openrc.py +5 -3
  82. pyinfra/operations/pacman.py +6 -7
  83. pyinfra/operations/pip.py +19 -12
  84. pyinfra/operations/pkg.py +3 -1
  85. pyinfra/operations/pkgin.py +5 -3
  86. pyinfra/operations/postgres.py +349 -0
  87. pyinfra/operations/postgresql.py +18 -335
  88. pyinfra/operations/puppet.py +3 -1
  89. pyinfra/operations/python.py +8 -19
  90. pyinfra/operations/runit.py +182 -0
  91. pyinfra/operations/selinux.py +47 -29
  92. pyinfra/operations/server.py +138 -67
  93. pyinfra/operations/snap.py +3 -1
  94. pyinfra/operations/ssh.py +18 -16
  95. pyinfra/operations/systemd.py +18 -12
  96. pyinfra/operations/sysvinit.py +7 -5
  97. pyinfra/operations/upstart.py +7 -5
  98. pyinfra/operations/util/__init__.py +12 -0
  99. pyinfra/operations/util/docker.py +177 -0
  100. pyinfra/operations/util/files.py +24 -16
  101. pyinfra/operations/util/packaging.py +54 -38
  102. pyinfra/operations/util/service.py +39 -47
  103. pyinfra/operations/vzctl.py +12 -10
  104. pyinfra/operations/xbps.py +5 -3
  105. pyinfra/operations/yum.py +15 -19
  106. pyinfra/operations/zypper.py +9 -10
  107. pyinfra/version.py +5 -2
  108. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/METADATA +51 -58
  109. pyinfra-3.0.1.dist-info/RECORD +168 -0
  110. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/WHEEL +1 -1
  111. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/entry_points.txt +0 -3
  112. pyinfra_cli/__main__.py +4 -3
  113. pyinfra_cli/commands.py +3 -2
  114. pyinfra_cli/exceptions.py +75 -43
  115. pyinfra_cli/inventory.py +52 -31
  116. pyinfra_cli/log.py +10 -2
  117. pyinfra_cli/main.py +88 -65
  118. pyinfra_cli/prints.py +37 -109
  119. pyinfra_cli/util.py +15 -10
  120. tests/test_api/test_api.py +2 -0
  121. tests/test_api/test_api_arguments.py +9 -9
  122. tests/test_api/test_api_deploys.py +15 -19
  123. tests/test_api/test_api_facts.py +4 -5
  124. tests/test_api/test_api_operations.py +18 -20
  125. tests/test_api/test_api_util.py +41 -2
  126. tests/test_cli/test_cli.py +14 -50
  127. tests/test_cli/test_cli_deploy.py +10 -12
  128. tests/test_cli/test_cli_exceptions.py +50 -19
  129. tests/test_cli/test_cli_inventory.py +66 -0
  130. tests/test_cli/util.py +1 -1
  131. tests/test_connectors/test_dockerssh.py +11 -8
  132. tests/test_connectors/test_ssh.py +88 -23
  133. tests/test_connectors/test_sshuserclient.py +1 -1
  134. tests/test_connectors/test_terraform.py +11 -8
  135. tests/test_connectors/test_vagrant.py +6 -6
  136. pyinfra/connectors/ansible.py +0 -175
  137. pyinfra/connectors/mech.py +0 -189
  138. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  139. pyinfra/connectors/winrm.py +0 -312
  140. pyinfra/facts/windows.py +0 -366
  141. pyinfra/facts/windows_files.py +0 -90
  142. pyinfra/operations/windows.py +0 -59
  143. pyinfra/operations/windows_files.py +0 -538
  144. pyinfra-3.0.dev0.dist-info/RECORD +0 -170
  145. tests/test_connectors/test_ansible.py +0 -64
  146. tests/test_connectors/test_mech.py +0 -126
  147. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/LICENSE.md +0 -0
  148. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/top_level.txt +0 -0
pyinfra_cli/util.py CHANGED
@@ -58,19 +58,20 @@ def exec_file(filename, return_locals: bool = False, is_deploy_code: bool = Fals
58
58
  PYTHON_CODES[filename] = code
59
59
 
60
60
  # Create some base attributes for our "module"
61
- data = {
62
- "__file__": filename,
63
- }
61
+ data = {"__file__": filename}
64
62
 
65
63
  # Execute the code with locals/globals going into the dict above
66
64
  try:
67
65
  exec(PYTHON_CODES[filename], data)
66
+ except PyinfraError:
67
+ # Raise pyinfra errors as-is
68
+ raise
68
69
  except Exception as e:
69
- if isinstance(e, (PyinfraError, UnexpectedExternalError)):
70
- raise
70
+ # Wrap & re-raise errors in user code so we highlight filename/etc
71
71
  raise UnexpectedExternalError(e, filename)
72
+ finally:
73
+ state.current_exec_filename = old_current_exec_filename
72
74
 
73
- state.current_exec_filename = old_current_exec_filename
74
75
  return data
75
76
 
76
77
 
@@ -149,13 +150,15 @@ def parse_cli_arg(arg):
149
150
  return arg
150
151
 
151
152
 
152
- def try_import_module_attribute(path, prefix=None, raise_for_no_module=True):
153
+ def try_import_module_attribute(path, prefix=None, raise_for_none=True):
153
154
  if ":" in path:
154
155
  # Allow a.module.name:function syntax
155
156
  mod_path, attr_name = path.rsplit(":", 1)
156
- else:
157
+ elif "." in path:
157
158
  # And also a.module.name.function
158
159
  mod_path, attr_name = path.rsplit(".", 1)
160
+ else:
161
+ return None
159
162
 
160
163
  possible_modules = [mod_path]
161
164
  if prefix:
@@ -176,13 +179,15 @@ def try_import_module_attribute(path, prefix=None, raise_for_no_module=True):
176
179
  break
177
180
 
178
181
  if module is None:
179
- if raise_for_no_module:
182
+ if raise_for_none:
180
183
  raise CliError(f"No such module: {possible_modules[-1]}")
181
184
  return
182
185
 
183
186
  attr = getattr(module, attr_name, None)
184
187
  if attr is None:
185
- raise CliError(f"No such attribute in module {possible_modules[-1]}: {attr_name}")
188
+ if raise_for_none:
189
+ raise CliError(f"No such attribute in module {possible_modules[-1]}: {attr_name}")
190
+ return
186
191
 
187
192
  return attr
188
193
 
@@ -1,4 +1,5 @@
1
1
  from unittest import TestCase
2
+ from unittest.mock import patch
2
3
 
3
4
  from paramiko import SSHException
4
5
 
@@ -60,6 +61,7 @@ class TestInventoryApi(TestCase):
60
61
 
61
62
 
62
63
  class TestStateApi(PatchSSHTestCase):
64
+ @patch("pyinfra.connectors.base.raise_if_bad_type", lambda *args, **kwargs: None)
63
65
  def test_fail_percent(self):
64
66
  inventory = make_inventory(
65
67
  (
@@ -16,40 +16,40 @@ class TestOperationKwargs(TestCase):
16
16
 
17
17
  def test_get_from_host(self):
18
18
  config = Config(SUDO="config-value")
19
- inventory = Inventory(([("somehost", {"_sudo": "host-value"})], {}))
19
+ inventory = Inventory(([("somehost", {"_sudo": True})], {}))
20
20
 
21
21
  state = State(config=config, inventory=inventory)
22
22
 
23
23
  kwargs, keys = pop_global_arguments({}, state=state, host=inventory.get_host("somehost"))
24
- assert kwargs["_sudo"] == "host-value"
24
+ assert kwargs["_sudo"] is True
25
25
 
26
26
  def test_get_from_state_deploy_kwargs(self):
27
27
  config = Config(SUDO="config-value")
28
- inventory = Inventory(([("somehost", {"_sudo": "host-value"})], {}))
28
+ inventory = Inventory(([("somehost", {"_sudo": False})], {}))
29
29
  somehost = inventory.get_host("somehost")
30
30
 
31
31
  state = State(config=config, inventory=inventory)
32
- somehost.current_deploy_kwargs = {"_sudo": "deploy-kwarg-value"}
32
+ somehost.current_deploy_kwargs = {"_sudo": True}
33
33
 
34
34
  kwargs, keys = pop_global_arguments({}, state=state, host=somehost)
35
- assert kwargs["_sudo"] == "deploy-kwarg-value"
35
+ assert kwargs["_sudo"] is True
36
36
 
37
37
  def test_get_from_kwargs(self):
38
38
  config = Config(SUDO="config-value")
39
- inventory = Inventory(([("somehost", {"_sudo": "host-value"})], {}))
39
+ inventory = Inventory(([("somehost", {"_sudo": False})], {}))
40
40
  somehost = inventory.get_host("somehost")
41
41
 
42
42
  state = State(config=config, inventory=inventory)
43
43
  somehost.current_deploy_kwargs = {
44
- "_sudo": "deploy-kwarg-value",
44
+ "_sudo": False,
45
45
  "_sudo_user": "deploy-kwarg-user",
46
46
  }
47
47
 
48
48
  kwargs, keys = pop_global_arguments(
49
- {"_sudo": "kwarg-value"},
49
+ {"_sudo": True},
50
50
  state=state,
51
51
  host=somehost,
52
52
  )
53
- assert kwargs["_sudo"] == "kwarg-value"
53
+ assert kwargs["_sudo"] is True
54
54
  assert kwargs["_sudo_user"] == "deploy-kwarg-user"
55
55
  assert "_sudo" in keys
@@ -24,7 +24,7 @@ class TestDeploysApi(PatchSSHTestCase):
24
24
 
25
25
  connect_all(state)
26
26
 
27
- @deploy
27
+ @deploy()
28
28
  def test_deploy(state=None, host=None):
29
29
  server.shell(commands=["echo first command"])
30
30
  server.shell(commands=["echo second command"])
@@ -40,20 +40,20 @@ class TestDeploysApi(PatchSSHTestCase):
40
40
  run_ops(state)
41
41
 
42
42
  first_op_hash = op_order[0]
43
- assert state.op_meta[first_op_hash].names == {"test_deploy | Server/Shell"}
44
- assert state.ops[somehost][first_op_hash].operation_meta.commands == [
43
+ assert state.op_meta[first_op_hash].names == {"test_deploy | server.shell"}
44
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
45
45
  StringCommand("echo first command"),
46
46
  ]
47
- assert state.ops[anotherhost][first_op_hash].operation_meta.commands == [
47
+ assert state.ops[anotherhost][first_op_hash].operation_meta._commands == [
48
48
  StringCommand("echo first command"),
49
49
  ]
50
50
 
51
51
  second_op_hash = op_order[1]
52
- assert state.op_meta[second_op_hash].names == {"test_deploy | Server/Shell"}
53
- assert state.ops[somehost][second_op_hash].operation_meta.commands == [
52
+ assert state.op_meta[second_op_hash].names == {"test_deploy | server.shell"}
53
+ assert state.ops[somehost][second_op_hash].operation_meta._commands == [
54
54
  StringCommand("echo second command"),
55
55
  ]
56
- assert state.ops[anotherhost][second_op_hash].operation_meta.commands == [
56
+ assert state.ops[anotherhost][second_op_hash].operation_meta._commands == [
57
57
  StringCommand("echo second command"),
58
58
  ]
59
59
 
@@ -67,10 +67,6 @@ class TestDeploysApi(PatchSSHTestCase):
67
67
  assert state.results[somehost].error_ops == 0
68
68
  assert state.results[anotherhost].error_ops == 0
69
69
 
70
- # And with the different modes
71
- run_ops(state, serial=True)
72
- run_ops(state, no_wait=True)
73
-
74
70
  disconnect_all(state)
75
71
 
76
72
  def test_nested_deploy(self):
@@ -87,11 +83,11 @@ class TestDeploysApi(PatchSSHTestCase):
87
83
 
88
84
  connect_all(state)
89
85
 
90
- @deploy
86
+ @deploy()
91
87
  def test_nested_deploy():
92
88
  server.shell(commands=["echo nested command"])
93
89
 
94
- @deploy
90
+ @deploy()
95
91
  def test_deploy():
96
92
  server.shell(commands=["echo first command"])
97
93
  test_nested_deploy()
@@ -108,21 +104,21 @@ class TestDeploysApi(PatchSSHTestCase):
108
104
  run_ops(state)
109
105
 
110
106
  first_op_hash = op_order[0]
111
- assert state.op_meta[first_op_hash].names == {"test_deploy | Server/Shell"}
112
- assert state.ops[somehost][first_op_hash].operation_meta.commands == [
107
+ assert state.op_meta[first_op_hash].names == {"test_deploy | server.shell"}
108
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
113
109
  StringCommand("echo first command"),
114
110
  ]
115
111
 
116
112
  second_op_hash = op_order[1]
117
113
  assert state.op_meta[second_op_hash].names == {
118
- "test_deploy | test_nested_deploy | Server/Shell",
114
+ "test_deploy | test_nested_deploy | server.shell",
119
115
  }
120
- assert state.ops[somehost][second_op_hash].operation_meta.commands == [
116
+ assert state.ops[somehost][second_op_hash].operation_meta._commands == [
121
117
  StringCommand("echo nested command"),
122
118
  ]
123
119
 
124
120
  third_op_hash = op_order[2]
125
- assert state.op_meta[third_op_hash].names == {"test_deploy | Server/Shell"}
126
- assert state.ops[somehost][third_op_hash].operation_meta.commands == [
121
+ assert state.op_meta[third_op_hash].names == {"test_deploy | server.shell"}
122
+ assert state.ops[somehost][third_op_hash].operation_meta._commands == [
127
123
  StringCommand("echo second command"),
128
124
  ]
@@ -1,7 +1,7 @@
1
1
  from unittest.mock import MagicMock, patch
2
2
 
3
3
  from pyinfra.api import Config, State
4
- from pyinfra.api.arguments import get_connector_argument_keys, pop_global_arguments
4
+ from pyinfra.api.arguments import CONNECTOR_ARGUMENT_KEYS, pop_global_arguments
5
5
  from pyinfra.api.connect import connect_all
6
6
  from pyinfra.api.exceptions import PyinfraError
7
7
  from pyinfra.api.facts import get_facts
@@ -17,7 +17,7 @@ def _get_executor_defaults(state, host):
17
17
  return {
18
18
  key: value
19
19
  for key, value in global_argument_defaults.items()
20
- if key in get_connector_argument_keys()
20
+ if key in CONNECTOR_ARGUMENT_KEYS
21
21
  }
22
22
 
23
23
 
@@ -55,7 +55,6 @@ class TestFactsApi(PatchSSHTestCase):
55
55
  anotherhost.current_op_global_arguments = {
56
56
  "_sudo": True,
57
57
  "_sudo_user": "someuser",
58
- "_use_sudo_password": True,
59
58
  "_su_user": "someuser",
60
59
  "_timeout": 10,
61
60
  "_env": {"HELLO": "WORLD"},
@@ -237,7 +236,7 @@ class TestFactsApi(PatchSSHTestCase):
237
236
 
238
237
  assert fact_data == {host_1: "some-output"}
239
238
  fake_run_command.assert_called_with(
240
- Arch.command,
239
+ Arch().command(),
241
240
  print_input=False,
242
241
  print_output=False,
243
242
  **defaults,
@@ -310,7 +309,7 @@ class TestHostFactsApi(PatchSSHTestCase):
310
309
 
311
310
  assert fact_data == "some-output"
312
311
  fake_run_command.assert_called_with(
313
- Arch.command,
312
+ Arch().command(),
314
313
  print_input=False,
315
314
  print_output=False,
316
315
  **defaults,
@@ -29,11 +29,11 @@ from ..util import make_inventory
29
29
  class TestOperationMeta(TestCase):
30
30
  def test_operation_meta_repr_no_change(self):
31
31
  op_meta = OperationMeta("hash", False)
32
- assert repr(op_meta) == "OperationMeta(changed=False, hash=hash)"
32
+ assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=False, hash=hash)"
33
33
 
34
34
  def test_operation_meta_repr_changes(self):
35
35
  op_meta = OperationMeta("hash", True)
36
- assert repr(op_meta) == "OperationMeta(changed=True, hash=hash)"
36
+ assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=True, hash=hash)"
37
37
 
38
38
 
39
39
  class TestOperationsApi(PatchSSHTestCase):
@@ -78,7 +78,7 @@ class TestOperationsApi(PatchSSHTestCase):
78
78
  first_op_hash = op_order[0]
79
79
 
80
80
  # Ensure the op name
81
- assert state.op_meta[first_op_hash].names == {"Files/File"}
81
+ assert state.op_meta[first_op_hash].names == {"files.file"}
82
82
 
83
83
  # Ensure the global kwargs (same for both hosts)
84
84
  somehost_global_arguments = state.ops[somehost][first_op_hash].global_arguments
@@ -97,7 +97,7 @@ class TestOperationsApi(PatchSSHTestCase):
97
97
  run_ops(state)
98
98
 
99
99
  # Ensure the commands
100
- assert state.ops[somehost][first_op_hash].operation_meta.commands == [
100
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
101
101
  StringCommand("touch /var/log/pyinfra.log"),
102
102
  StringCommand("chmod 644 /var/log/pyinfra.log"),
103
103
  StringCommand("chown pyinfra:pyinfra /var/log/pyinfra.log"),
@@ -113,10 +113,6 @@ class TestOperationsApi(PatchSSHTestCase):
113
113
  assert state.results[somehost].error_ops == 0
114
114
  assert state.results[anotherhost].error_ops == 0
115
115
 
116
- # And with the different modes
117
- run_ops(state, serial=True)
118
- run_ops(state, no_wait=True)
119
-
120
116
  disconnect_all(state)
121
117
 
122
118
  @patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
@@ -181,7 +177,7 @@ class TestOperationsApi(PatchSSHTestCase):
181
177
  run_ops(state)
182
178
 
183
179
  # Ensure first op used the right (upload) command
184
- assert state.ops[somehost][first_op_hash].operation_meta.commands == [
180
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
185
181
  StringCommand("mkdir -p /home/vagrant"),
186
182
  FileUploadCommand("files/file.txt", "/home/vagrant/file.txt"),
187
183
  ]
@@ -225,7 +221,7 @@ class TestOperationsApi(PatchSSHTestCase):
225
221
  run_ops(state)
226
222
 
227
223
  # Ensure first op has the right (upload) command
228
- assert state.ops[somehost][first_op_hash].operation_meta.commands == [
224
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
229
225
  FileDownloadCommand("/home/vagrant/file.txt", "files/file.txt"),
230
226
  ]
231
227
 
@@ -296,7 +292,7 @@ class TestOperationsApi(PatchSSHTestCase):
296
292
  fake_run_local_process.assert_called_with(
297
293
  (
298
294
  "rsync -ax --delete --rsh "
299
- '"ssh -o BatchMode=yes"'
295
+ '"ssh -o BatchMode=yes -o \\"StrictHostKeyChecking=accept-new\\""'
300
296
  " --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
301
297
  ),
302
298
  print_output=False,
@@ -381,7 +377,8 @@ class TestOperationsApi(PatchSSHTestCase):
381
377
  fake_run_local_process.assert_called_with(
382
378
  (
383
379
  "rsync -ax --delete --rsh "
384
- "\"ssh -o BatchMode=yes -F '/home/me/ssh_test_config && echo hi'\""
380
+ '"ssh -o BatchMode=yes -o \\"StrictHostKeyChecking=accept-new\\" '
381
+ "-F '/home/me/ssh_test_config && echo hi'\""
385
382
  " --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
386
383
  ),
387
384
  print_output=False,
@@ -393,7 +390,7 @@ class TestOperationsApi(PatchSSHTestCase):
393
390
  state = State(inventory, Config())
394
391
  connect_all(state)
395
392
 
396
- with patch("pyinfra.connectors.ssh.find_executable", lambda x: None):
393
+ with patch("pyinfra.connectors.ssh.which", lambda x: None):
397
394
  with self.assertRaises(OperationError) as context:
398
395
  add_op(state, files.rsync, "src", "dest")
399
396
 
@@ -436,11 +433,11 @@ class TestNestedOperationsApi(PatchSSHTestCase):
436
433
 
437
434
  try:
438
435
  outer_result = server.shell(commands="echo outer")
439
- assert outer_result.combined_output_lines is None
436
+ assert outer_result._combined_output is None
440
437
 
441
438
  def callback():
442
439
  inner_result = server.shell(commands="echo inner")
443
- assert inner_result.combined_output_lines is not None
440
+ assert inner_result._combined_output is not None
444
441
 
445
442
  python.call(function=callback)
446
443
 
@@ -450,7 +447,7 @@ class TestNestedOperationsApi(PatchSSHTestCase):
450
447
 
451
448
  assert len(state.get_op_order()) == 3
452
449
  assert state.results[somehost].success_ops == 3
453
- assert outer_result.combined_output_lines is not None
450
+ assert outer_result._combined_output is not None
454
451
 
455
452
  disconnect_all(state)
456
453
  finally:
@@ -532,18 +529,19 @@ class TestOperationOrdering(PatchSSHTestCase):
532
529
  # Add op to just the second host - using the context modules such that
533
530
  # it replicates a deploy file.
534
531
  ctx_host.set(inventory.get_host("anotherhost"))
535
- first_context_hash = server.user("anotherhost_user").hash
532
+ first_context_hash = server.user("anotherhost_user")._hash
536
533
 
537
534
  # Add op to just the first host - using the context modules such that
538
535
  # it replicates a deploy file.
539
536
  ctx_host.set(inventory.get_host("somehost"))
540
- second_context_hash = server.user("somehost_user").hash
537
+ second_context_hash = server.user("somehost_user")._hash
541
538
 
542
539
  ctx_state.reset()
543
540
  ctx_host.reset()
544
541
 
545
542
  pyinfra.is_cli = False
546
543
 
544
+ print(state.ops)
547
545
  # Ensure there are two ops
548
546
  op_order = state.get_op_order()
549
547
  assert len(op_order) == 3
@@ -567,9 +565,9 @@ class TestOperationOrdering(PatchSSHTestCase):
567
565
  another_host = inventory.get_host("anotherhost")
568
566
 
569
567
  def add_another_op():
570
- return add_op(state, server.shell, "echo second-op")[another_host].hash
568
+ return add_op(state, server.shell, "echo second-op")[another_host]._hash
571
569
 
572
- first_op_hash = add_op(state, server.shell, "echo first-op")[another_host].hash
570
+ first_op_hash = add_op(state, server.shell, "echo first-op")[another_host]._hash
573
571
  second_op_hash = add_another_op() # note `add_op` will be called on an earlier line
574
572
 
575
573
  op_order = state.get_op_order()
@@ -1,6 +1,7 @@
1
+ from io import BytesIO, StringIO
1
2
  from unittest import TestCase
2
3
 
3
- from pyinfra.api.util import format_exception, get_caller_frameinfo, try_int
4
+ from pyinfra.api.util import format_exception, get_caller_frameinfo, get_file_io, try_int
4
5
 
5
6
 
6
7
  class TestApiUtil(TestCase):
@@ -15,8 +16,46 @@ class TestApiUtil(TestCase):
15
16
  return get_caller_frameinfo()
16
17
 
17
18
  frameinfo = _get_caller_frameinfo()
18
- assert frameinfo.lineno == 17 # called by the line above
19
+ assert frameinfo.lineno == 18 # called by the line above
19
20
 
20
21
  def test_format_exception(self):
21
22
  exception = Exception("I am a message", 1)
22
23
  assert format_exception(exception) == "Exception('I am a message', 1)"
24
+
25
+
26
+ class TestApiUtilFileIO(TestCase):
27
+ def test_get_file_io_stringio_to_string(self):
28
+ file = StringIO("some string")
29
+
30
+ with get_file_io(file, mode="r") as f:
31
+ data = f.read()
32
+
33
+ assert isinstance(data, str)
34
+ assert data == "some string"
35
+
36
+ def test_get_file_io_stringio_to_bytes(self):
37
+ file = StringIO("some string")
38
+
39
+ with get_file_io(file) as f:
40
+ data = f.read()
41
+
42
+ assert isinstance(data, bytes)
43
+ assert data == b"some string"
44
+
45
+ def test_get_file_io_bytesio_to_bytes(self):
46
+ file = BytesIO(b"some string")
47
+
48
+ with get_file_io(file) as f:
49
+ data = f.read()
50
+
51
+ assert isinstance(data, bytes)
52
+ assert data == b"some string"
53
+
54
+ def test_get_file_io_bytesio_to_string(self):
55
+ file = BytesIO(b"some string")
56
+
57
+ with get_file_io(file, mode="r") as f:
58
+ data = f.read()
59
+
60
+ assert isinstance(data, str)
61
+ assert data == "some string"
@@ -1,7 +1,6 @@
1
1
  from os import path
2
2
  from unittest import TestCase
3
3
 
4
- from pyinfra.context import ctx_state
5
4
  from pyinfra_cli.main import _main
6
5
 
7
6
  from ..paramiko_util import PatchSSHTestCase
@@ -17,23 +16,10 @@ class TestCliEagerFlags(TestCase):
17
16
  assert result.exit_code == 0, result.stdout
18
17
 
19
18
 
20
- class TestDeployCli(PatchSSHTestCase):
21
- def setUp(self):
22
- ctx_state.reset()
23
-
24
- def test_invalid_deploy(self):
25
- result = run_cli(
26
- "@local",
27
- "not-a-file.py",
28
- )
29
- assert result.exit_code == 1, result.stdout
30
- assert "No deploy file: not-a-file.py" in result.stdout
31
-
32
-
33
19
  class TestOperationCli(PatchSSHTestCase):
34
20
  def test_invalid_operation_module(self):
35
21
  result = run_cli(
36
- path.join("tests", "deploy", "inventories", "inventory.py"),
22
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
37
23
  "not_a_module.shell",
38
24
  )
39
25
  assert result.exit_code == 1, result.stdout
@@ -41,7 +27,7 @@ class TestOperationCli(PatchSSHTestCase):
41
27
 
42
28
  def test_invalid_operation_function(self):
43
29
  result = run_cli(
44
- path.join("tests", "deploy", "inventories", "inventory.py"),
30
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
45
31
  "server.not_an_operation",
46
32
  )
47
33
  assert result.exit_code == 1, result.stdout
@@ -50,7 +36,7 @@ class TestOperationCli(PatchSSHTestCase):
50
36
  def test_deploy_operation(self):
51
37
  result = run_cli(
52
38
  "-y",
53
- path.join("tests", "deploy", "inventories", "inventory.py"),
39
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
54
40
  "server.shell",
55
41
  "echo hi",
56
42
  )
@@ -59,7 +45,7 @@ class TestOperationCli(PatchSSHTestCase):
59
45
  def test_deploy_operation_with_all(self):
60
46
  result = run_cli(
61
47
  "-y",
62
- path.join("tests", "deploy", "inventory_all.py"),
48
+ path.join("tests", "test_cli", "deploy", "inventory_all.py"),
63
49
  "server.shell",
64
50
  "echo hi",
65
51
  )
@@ -68,7 +54,7 @@ class TestOperationCli(PatchSSHTestCase):
68
54
  def test_deploy_operation_json_args(self):
69
55
  result = run_cli(
70
56
  "-y",
71
- path.join("tests", "deploy", "inventory_all.py"),
57
+ path.join("tests", "test_cli", "deploy", "inventory_all.py"),
72
58
  "server.shell",
73
59
  '[["echo hi"], {}]',
74
60
  )
@@ -78,7 +64,7 @@ class TestOperationCli(PatchSSHTestCase):
78
64
  class TestFactCli(PatchSSHTestCase):
79
65
  def test_get_fact(self):
80
66
  result = run_cli(
81
- path.join("tests", "deploy", "inventories", "inventory.py"),
67
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
82
68
  "fact",
83
69
  "server.Os",
84
70
  )
@@ -87,7 +73,7 @@ class TestFactCli(PatchSSHTestCase):
87
73
 
88
74
  def test_get_fact_with_kwargs(self):
89
75
  result = run_cli(
90
- path.join("tests", "deploy", "inventories", "inventory.py"),
76
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
91
77
  "fact",
92
78
  "files.File",
93
79
  "path=.",
@@ -95,29 +81,11 @@ class TestFactCli(PatchSSHTestCase):
95
81
  assert result.exit_code == 0, result.stdout
96
82
  assert '"somehost": null' in result.stdout
97
83
 
98
- def test_invalid_fact_module(self):
99
- result = run_cli(
100
- path.join("tests", "deploy", "inventories", "inventory.py"),
101
- "fact",
102
- "not_a_module.NotAFact",
103
- )
104
- assert result.exit_code == 1, result.stdout
105
- assert "No such module: pyinfra.facts.not_a_module" in result.stdout
106
-
107
- def test_invalid_fact_class(self):
108
- result = run_cli(
109
- path.join("tests", "deploy", "inventories", "inventory.py"),
110
- "fact",
111
- "server.NotAFact",
112
- )
113
- assert result.exit_code == 1, result.stdout
114
- assert "No such attribute in module pyinfra.facts.server: NotAFact" in result.stdout
115
-
116
84
 
117
85
  class TestExecCli(PatchSSHTestCase):
118
86
  def test_exec_command(self):
119
87
  result = run_cli(
120
- path.join("tests", "deploy", "inventories", "inventory.py"),
88
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
121
89
  "exec",
122
90
  "--",
123
91
  "echo hi",
@@ -126,7 +94,7 @@ class TestExecCli(PatchSSHTestCase):
126
94
 
127
95
  def test_exec_command_with_options(self):
128
96
  result = run_cli(
129
- path.join("tests", "deploy", "inventories", "inventory.py"),
97
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
130
98
  "exec",
131
99
  "--sudo",
132
100
  "--sudo-user",
@@ -144,7 +112,7 @@ class TestExecCli(PatchSSHTestCase):
144
112
 
145
113
  def test_exec_command_with_serial(self):
146
114
  result = run_cli(
147
- path.join("tests", "deploy", "inventories", "inventory.py"),
115
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
148
116
  "exec",
149
117
  "--serial",
150
118
  "--",
@@ -154,7 +122,7 @@ class TestExecCli(PatchSSHTestCase):
154
122
 
155
123
  def test_exec_command_with_no_wait(self):
156
124
  result = run_cli(
157
- path.join("tests", "deploy", "inventories", "inventory.py"),
125
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
158
126
  "exec",
159
127
  "--no-wait",
160
128
  "--",
@@ -164,7 +132,7 @@ class TestExecCli(PatchSSHTestCase):
164
132
 
165
133
  def test_exec_command_with_debug_operations(self):
166
134
  result = run_cli(
167
- path.join("tests", "deploy", "inventories", "inventory.py"),
135
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
168
136
  "exec",
169
137
  "--debug-operations",
170
138
  "--",
@@ -174,7 +142,7 @@ class TestExecCli(PatchSSHTestCase):
174
142
 
175
143
  def test_exec_command_with_debug_facts(self):
176
144
  result = run_cli(
177
- path.join("tests", "deploy", "inventories", "inventory.py"),
145
+ path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
178
146
  "exec",
179
147
  "--debug-facts",
180
148
  "--",
@@ -213,15 +181,11 @@ class TestDirectMainExecution(PatchSSHTestCase):
213
181
  limit=None,
214
182
  no_wait=False,
215
183
  serial=False,
216
- winrm_username=None,
217
- winrm_password=None,
218
- winrm_port=None,
219
- winrm_transport=None,
220
184
  shell_executable=None,
221
- quiet=False,
222
185
  data=tuple(),
223
186
  debug=False,
224
187
  debug_facts=False,
188
+ debug_all=False,
225
189
  debug_operations=False,
226
190
  config_filename="config.py",
227
191
  )