pyinfra 2.9.2__py2.py3-none-any.whl → 3.0__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 (156) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +265 -253
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +68 -53
  5. pyinfra/api/config.py +139 -32
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +7 -26
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +102 -137
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +21 -25
  13. pyinfra/api/operation.py +240 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +79 -86
  17. pyinfra/connectors/base.py +147 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +220 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +196 -208
  22. pyinfra/connectors/ssh.py +530 -613
  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 +211 -137
  27. pyinfra/connectors/vagrant.py +60 -53
  28. pyinfra/context.py +4 -2
  29. pyinfra/facts/apk.py +2 -0
  30. pyinfra/facts/apt.py +2 -0
  31. pyinfra/facts/brew.py +2 -0
  32. pyinfra/facts/bsdinit.py +2 -0
  33. pyinfra/facts/cargo.py +2 -0
  34. pyinfra/facts/choco.py +2 -0
  35. pyinfra/facts/deb.py +7 -2
  36. pyinfra/facts/dnf.py +2 -0
  37. pyinfra/facts/docker.py +19 -0
  38. pyinfra/facts/files.py +47 -32
  39. pyinfra/facts/gem.py +2 -0
  40. pyinfra/facts/git.py +3 -1
  41. pyinfra/facts/gpg.py +3 -1
  42. pyinfra/facts/hardware.py +34 -24
  43. pyinfra/facts/iptables.py +5 -3
  44. pyinfra/facts/launchd.py +2 -0
  45. pyinfra/facts/lxd.py +2 -0
  46. pyinfra/facts/mysql.py +13 -6
  47. pyinfra/facts/npm.py +1 -0
  48. pyinfra/facts/openrc.py +2 -0
  49. pyinfra/facts/pacman.py +6 -2
  50. pyinfra/facts/pip.py +2 -0
  51. pyinfra/facts/pkg.py +2 -0
  52. pyinfra/facts/pkgin.py +2 -0
  53. pyinfra/facts/postgres.py +168 -0
  54. pyinfra/facts/postgresql.py +6 -160
  55. pyinfra/facts/rpm.py +12 -9
  56. pyinfra/facts/runit.py +68 -0
  57. pyinfra/facts/selinux.py +3 -1
  58. pyinfra/facts/server.py +80 -36
  59. pyinfra/facts/snap.py +2 -0
  60. pyinfra/facts/systemd.py +31 -12
  61. pyinfra/facts/sysvinit.py +10 -10
  62. pyinfra/facts/upstart.py +2 -0
  63. pyinfra/facts/util/packaging.py +7 -4
  64. pyinfra/facts/vzctl.py +2 -0
  65. pyinfra/facts/xbps.py +2 -0
  66. pyinfra/facts/yum.py +2 -0
  67. pyinfra/facts/zypper.py +2 -0
  68. pyinfra/local.py +4 -5
  69. pyinfra/operations/apk.py +6 -4
  70. pyinfra/operations/apt.py +46 -65
  71. pyinfra/operations/brew.py +17 -22
  72. pyinfra/operations/bsdinit.py +9 -7
  73. pyinfra/operations/cargo.py +4 -2
  74. pyinfra/operations/choco.py +4 -2
  75. pyinfra/operations/dnf.py +19 -23
  76. pyinfra/operations/docker.py +339 -0
  77. pyinfra/operations/files.py +188 -386
  78. pyinfra/operations/gem.py +4 -2
  79. pyinfra/operations/git.py +24 -53
  80. pyinfra/operations/iptables.py +29 -35
  81. pyinfra/operations/launchd.py +6 -7
  82. pyinfra/operations/lxd.py +8 -13
  83. pyinfra/operations/mysql.py +62 -81
  84. pyinfra/operations/npm.py +9 -2
  85. pyinfra/operations/openrc.py +6 -4
  86. pyinfra/operations/pacman.py +7 -8
  87. pyinfra/operations/pip.py +25 -24
  88. pyinfra/operations/pkg.py +4 -2
  89. pyinfra/operations/pkgin.py +6 -4
  90. pyinfra/operations/postgres.py +349 -0
  91. pyinfra/operations/postgresql.py +18 -379
  92. pyinfra/operations/puppet.py +3 -1
  93. pyinfra/operations/python.py +8 -19
  94. pyinfra/operations/runit.py +182 -0
  95. pyinfra/operations/selinux.py +47 -44
  96. pyinfra/operations/server.py +111 -127
  97. pyinfra/operations/snap.py +4 -4
  98. pyinfra/operations/ssh.py +20 -33
  99. pyinfra/operations/systemd.py +19 -15
  100. pyinfra/operations/sysvinit.py +9 -16
  101. pyinfra/operations/upstart.py +9 -7
  102. pyinfra/operations/util/__init__.py +12 -0
  103. pyinfra/operations/util/docker.py +177 -0
  104. pyinfra/operations/util/files.py +24 -16
  105. pyinfra/operations/util/packaging.py +55 -57
  106. pyinfra/operations/util/service.py +39 -51
  107. pyinfra/operations/vzctl.py +12 -10
  108. pyinfra/operations/xbps.py +6 -4
  109. pyinfra/operations/yum.py +18 -22
  110. pyinfra/operations/zypper.py +12 -13
  111. pyinfra/version.py +5 -2
  112. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
  113. pyinfra-3.0.dist-info/RECORD +167 -0
  114. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
  115. pyinfra-3.0.dist-info/entry_points.txt +11 -0
  116. pyinfra_cli/__main__.py +4 -3
  117. pyinfra_cli/commands.py +7 -2
  118. pyinfra_cli/exceptions.py +78 -42
  119. pyinfra_cli/inventory.py +40 -6
  120. pyinfra_cli/log.py +17 -3
  121. pyinfra_cli/main.py +133 -90
  122. pyinfra_cli/prints.py +95 -127
  123. pyinfra_cli/util.py +62 -29
  124. tests/test_api/test_api.py +2 -0
  125. tests/test_api/test_api_arguments.py +13 -13
  126. tests/test_api/test_api_deploys.py +28 -29
  127. tests/test_api/test_api_facts.py +60 -98
  128. tests/test_api/test_api_operations.py +101 -201
  129. tests/test_cli/test_cli.py +18 -49
  130. tests/test_cli/test_cli_deploy.py +11 -37
  131. tests/test_cli/test_cli_exceptions.py +50 -19
  132. tests/test_cli/util.py +1 -1
  133. tests/test_connectors/test_chroot.py +6 -6
  134. tests/test_connectors/test_docker.py +4 -4
  135. tests/test_connectors/test_dockerssh.py +38 -50
  136. tests/test_connectors/test_local.py +11 -12
  137. tests/test_connectors/test_ssh.py +105 -93
  138. tests/test_connectors/test_terraform.py +9 -15
  139. tests/test_connectors/test_util.py +24 -46
  140. tests/test_connectors/test_vagrant.py +7 -7
  141. pyinfra/api/operation.pyi +0 -117
  142. pyinfra/connectors/ansible.py +0 -171
  143. pyinfra/connectors/mech.py +0 -186
  144. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  145. pyinfra/connectors/winrm.py +0 -320
  146. pyinfra/facts/windows.py +0 -366
  147. pyinfra/facts/windows_files.py +0 -90
  148. pyinfra/operations/windows.py +0 -59
  149. pyinfra/operations/windows_files.py +0 -551
  150. pyinfra-2.9.2.dist-info/RECORD +0 -170
  151. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  152. tests/test_connectors/test_ansible.py +0 -64
  153. tests/test_connectors/test_mech.py +0 -126
  154. tests/test_connectors/test_winrm.py +0 -76
  155. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
  156. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,7 @@ from pyinfra.api.connect import connect_all, disconnect_all
18
18
  from pyinfra.api.exceptions import PyinfraError
19
19
  from pyinfra.api.operation import OperationMeta, add_op
20
20
  from pyinfra.api.operations import run_ops
21
+ from pyinfra.api.state import StateOperationMeta
21
22
  from pyinfra.context import ctx_host, ctx_state
22
23
  from pyinfra.operations import files, python, server
23
24
 
@@ -27,12 +28,12 @@ from ..util import make_inventory
27
28
 
28
29
  class TestOperationMeta(TestCase):
29
30
  def test_operation_meta_repr_no_change(self):
30
- op_meta = OperationMeta("hash", [])
31
- assert repr(op_meta) == "OperationMeta(commands=0, changed=False, hash=hash)"
31
+ op_meta = OperationMeta("hash", False)
32
+ assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=False, hash=hash)"
32
33
 
33
34
  def test_operation_meta_repr_changes(self):
34
- op_meta = OperationMeta("hash", ["a-command"])
35
- assert repr(op_meta) == "OperationMeta(commands=1, changed=True, hash=hash)"
35
+ op_meta = OperationMeta("hash", True)
36
+ assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=True, hash=hash)"
36
37
 
37
38
 
38
39
  class TestOperationsApi(PatchSSHTestCase):
@@ -60,11 +61,11 @@ class TestOperationsApi(PatchSSHTestCase):
60
61
  group="pyinfra",
61
62
  mode="644",
62
63
  create_remote_dir=False,
63
- sudo=True,
64
- sudo_user="test_sudo",
65
- su_user="test_su",
66
- ignore_errors=True,
67
- env={
64
+ _sudo=True,
65
+ _sudo_user="test_sudo",
66
+ _su_user="test_su",
67
+ _ignore_errors=True,
68
+ _env={
68
69
  "TEST": "what",
69
70
  },
70
71
  )
@@ -77,68 +78,43 @@ class TestOperationsApi(PatchSSHTestCase):
77
78
  first_op_hash = op_order[0]
78
79
 
79
80
  # Ensure the op name
80
- assert state.op_meta[first_op_hash]["names"] == {"Files/File"}
81
+ assert state.op_meta[first_op_hash].names == {"files.file"}
82
+
83
+ # Ensure the global kwargs (same for both hosts)
84
+ somehost_global_arguments = state.ops[somehost][first_op_hash].global_arguments
85
+ assert somehost_global_arguments["_sudo"] is True
86
+ assert somehost_global_arguments["_sudo_user"] == "test_sudo"
87
+ assert somehost_global_arguments["_su_user"] == "test_su"
88
+ assert somehost_global_arguments["_ignore_errors"] is True
89
+
90
+ anotherhost_global_arguments = state.ops[anotherhost][first_op_hash].global_arguments
91
+ assert anotherhost_global_arguments["_sudo"] is True
92
+ assert anotherhost_global_arguments["_sudo_user"] == "test_sudo"
93
+ assert anotherhost_global_arguments["_su_user"] == "test_su"
94
+ assert anotherhost_global_arguments["_ignore_errors"] is True
95
+
96
+ # Ensure run ops works
97
+ run_ops(state)
81
98
 
82
99
  # Ensure the commands
83
- assert state.ops[somehost][first_op_hash]["commands"] == [
100
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
84
101
  StringCommand("touch /var/log/pyinfra.log"),
85
102
  StringCommand("chmod 644 /var/log/pyinfra.log"),
86
103
  StringCommand("chown pyinfra:pyinfra /var/log/pyinfra.log"),
87
104
  ]
88
105
 
89
- # Ensure the global kwargs (same for both hosts)
90
- somehost_global_kwargs = state.ops[somehost][first_op_hash]["global_kwargs"]
91
- assert somehost_global_kwargs["sudo"] is True
92
- assert somehost_global_kwargs["sudo_user"] == "test_sudo"
93
- assert somehost_global_kwargs["su_user"] == "test_su"
94
- assert somehost_global_kwargs["ignore_errors"] is True
95
-
96
- anotherhost_global_kwargs = state.ops[anotherhost][first_op_hash]["global_kwargs"]
97
- assert anotherhost_global_kwargs["sudo"] is True
98
- assert anotherhost_global_kwargs["sudo_user"] == "test_sudo"
99
- assert anotherhost_global_kwargs["su_user"] == "test_su"
100
- assert anotherhost_global_kwargs["ignore_errors"] is True
101
-
102
- # Ensure run ops works
103
- run_ops(state)
104
-
105
106
  # Ensure ops completed OK
106
- assert state.results[somehost]["success_ops"] == 1
107
- assert state.results[somehost]["ops"] == 1
108
- assert state.results[anotherhost]["success_ops"] == 1
109
- assert state.results[anotherhost]["ops"] == 1
107
+ assert state.results[somehost].success_ops == 1
108
+ assert state.results[somehost].ops == 1
109
+ assert state.results[anotherhost].success_ops == 1
110
+ assert state.results[anotherhost].ops == 1
110
111
 
111
112
  # And w/o errors
112
- assert state.results[somehost]["error_ops"] == 0
113
- assert state.results[anotherhost]["error_ops"] == 0
114
-
115
- # And with the different modes
116
- run_ops(state, serial=True)
117
- run_ops(state, no_wait=True)
113
+ assert state.results[somehost].error_ops == 0
114
+ assert state.results[anotherhost].error_ops == 0
118
115
 
119
116
  disconnect_all(state)
120
117
 
121
- # def test_op_call_direct_falls(self):
122
- # inventory = make_inventory()
123
- # somehost = inventory.get_host('somehost')
124
- # state = State(inventory, Config())
125
-
126
- # # Enable printing on this test to catch any exceptions in the formatting
127
- # state.print_output = True
128
- # state.print_input = True
129
- # state.print_fact_info = True
130
- # state.print_noop_info = True
131
-
132
- # connect_all(state)
133
-
134
- # with self.assertRaises(PyinfraError) as context:
135
- # server.shell(commands='echo hi')
136
-
137
- # assert context.exception.args[0] == (
138
- # 'Operation order number not provided in API mode - '
139
- # 'you must use `add_op` to add operations.'
140
- # )
141
-
142
118
  @patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
143
119
  @patch("pyinfra.operations.files.os.path.isfile", lambda *args, **kwargs: True)
144
120
  def test_file_upload_op(self):
@@ -162,8 +138,8 @@ class TestOperationsApi(PatchSSHTestCase):
162
138
  files.put,
163
139
  src="files/file.txt",
164
140
  dest="/home/vagrant/file.txt",
165
- sudo=True,
166
- sudo_user="pyinfra",
141
+ _sudo=True,
142
+ _sudo_user="pyinfra",
167
143
  )
168
144
 
169
145
  # And with su
@@ -172,8 +148,8 @@ class TestOperationsApi(PatchSSHTestCase):
172
148
  files.put,
173
149
  src="files/file.txt",
174
150
  dest="/home/vagrant/file.txt",
175
- sudo=True,
176
- su_user="pyinfra",
151
+ _sudo=True,
152
+ _su_user="pyinfra",
177
153
  )
178
154
 
179
155
  op_order = state.get_op_order()
@@ -185,36 +161,36 @@ class TestOperationsApi(PatchSSHTestCase):
185
161
  second_op_hash = op_order[1]
186
162
 
187
163
  # Ensure first op is the right one
188
- assert state.op_meta[first_op_hash]["names"] == {"First op name"}
164
+ assert state.op_meta[first_op_hash].names == {"First op name"}
189
165
 
190
166
  somehost = inventory.get_host("somehost")
191
167
  anotherhost = inventory.get_host("anotherhost")
192
168
 
193
- # Ensure first op has the right (upload) command
194
- assert state.ops[somehost][first_op_hash]["commands"] == [
195
- StringCommand("mkdir -p /home/vagrant"),
196
- FileUploadCommand("files/file.txt", "/home/vagrant/file.txt"),
197
- ]
198
-
199
169
  # Ensure second op has sudo/sudo_user
200
- assert state.ops[somehost][second_op_hash]["global_kwargs"]["sudo"] is True
201
- assert state.ops[somehost][second_op_hash]["global_kwargs"]["sudo_user"] == "pyinfra"
170
+ assert state.ops[somehost][second_op_hash].global_arguments["_sudo"] is True
171
+ assert state.ops[somehost][second_op_hash].global_arguments["_sudo_user"] == "pyinfra"
202
172
 
203
173
  # Ensure third has su_user
204
- assert state.ops[somehost][op_order[2]]["global_kwargs"]["su_user"] == "pyinfra"
174
+ assert state.ops[somehost][op_order[2]].global_arguments["_su_user"] == "pyinfra"
205
175
 
206
176
  # Check run ops works
207
177
  run_ops(state)
208
178
 
179
+ # Ensure first op used the right (upload) command
180
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
181
+ StringCommand("mkdir -p /home/vagrant"),
182
+ FileUploadCommand("files/file.txt", "/home/vagrant/file.txt"),
183
+ ]
184
+
209
185
  # Ensure ops completed OK
210
- assert state.results[somehost]["success_ops"] == 3
211
- assert state.results[somehost]["ops"] == 3
212
- assert state.results[anotherhost]["success_ops"] == 3
213
- assert state.results[anotherhost]["ops"] == 3
186
+ assert state.results[somehost].success_ops == 3
187
+ assert state.results[somehost].ops == 3
188
+ assert state.results[anotherhost].success_ops == 3
189
+ assert state.results[anotherhost].ops == 3
214
190
 
215
191
  # And w/o errors
216
- assert state.results[somehost]["error_ops"] == 0
217
- assert state.results[anotherhost]["error_ops"] == 0
192
+ assert state.results[somehost].error_ops == 0
193
+ assert state.results[anotherhost].error_ops == 0
218
194
 
219
195
  def test_file_download_op(self):
220
196
  inventory = make_inventory()
@@ -236,25 +212,25 @@ class TestOperationsApi(PatchSSHTestCase):
236
212
  assert len(op_order) == 1
237
213
 
238
214
  first_op_hash = op_order[0]
239
- assert state.op_meta[first_op_hash]["names"] == {"First op name"}
215
+ assert state.op_meta[first_op_hash].names == {"First op name"}
240
216
 
241
217
  somehost = inventory.get_host("somehost")
242
218
  anotherhost = inventory.get_host("anotherhost")
243
219
 
220
+ with patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True):
221
+ run_ops(state)
222
+
244
223
  # Ensure first op has the right (upload) command
245
- assert state.ops[somehost][first_op_hash]["commands"] == [
224
+ assert state.ops[somehost][first_op_hash].operation_meta._commands == [
246
225
  FileDownloadCommand("/home/vagrant/file.txt", "files/file.txt"),
247
226
  ]
248
227
 
249
- with patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True):
250
- run_ops(state)
251
-
252
- assert state.results[somehost]["success_ops"] == 1
253
- assert state.results[somehost]["ops"] == 1
254
- assert state.results[anotherhost]["success_ops"] == 1
255
- assert state.results[anotherhost]["ops"] == 1
256
- assert state.results[somehost]["error_ops"] == 0
257
- assert state.results[anotherhost]["error_ops"] == 0
228
+ assert state.results[somehost].success_ops == 1
229
+ assert state.results[somehost].ops == 1
230
+ assert state.results[anotherhost].success_ops == 1
231
+ assert state.results[anotherhost].ops == 1
232
+ assert state.results[somehost].error_ops == 0
233
+ assert state.results[anotherhost].error_ops == 0
258
234
 
259
235
  def test_function_call_op(self):
260
236
  inventory = make_inventory()
@@ -283,7 +259,7 @@ class TestOperationsApi(PatchSSHTestCase):
283
259
  connect_all(state)
284
260
 
285
261
  # Add a run once op
286
- add_op(state, server.shell, 'echo "hi"', run_once=True, serial=True)
262
+ add_op(state, server.shell, 'echo "hi"', _run_once=True, _serial=True)
287
263
 
288
264
  # Ensure it's added to op_order
289
265
  assert len(state.get_op_order()) == 1
@@ -297,17 +273,15 @@ class TestOperationsApi(PatchSSHTestCase):
297
273
  # Check run works
298
274
  run_ops(state)
299
275
 
300
- assert (
301
- state.results[somehost]["success_ops"] + state.results[anotherhost]["success_ops"]
302
- ) == 1
276
+ assert (state.results[somehost].success_ops + state.results[anotherhost].success_ops) == 1
303
277
 
278
+ @patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
304
279
  def test_rsync_op(self):
305
280
  inventory = make_inventory(hosts=("somehost",))
306
281
  state = State(inventory, Config())
307
282
  connect_all(state)
308
283
 
309
- with patch("pyinfra.connectors.ssh.check_can_rsync"):
310
- add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
284
+ add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
311
285
 
312
286
  assert len(state.get_op_order()) == 1
313
287
 
@@ -318,20 +292,20 @@ class TestOperationsApi(PatchSSHTestCase):
318
292
  fake_run_local_process.assert_called_with(
319
293
  (
320
294
  "rsync -ax --delete --rsh "
321
- '"ssh -o BatchMode=yes"'
295
+ '"ssh -o BatchMode=yes -o \\"StrictHostKeyChecking=accept-new\\""'
322
296
  " --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
323
297
  ),
324
298
  print_output=False,
325
299
  print_prefix=inventory.get_host("somehost").print_prefix,
326
300
  )
327
301
 
302
+ @patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
328
303
  def test_rsync_op_with_strict_host_key_checking_disabled(self):
329
304
  inventory = make_inventory(hosts=(("somehost", {"ssh_strict_host_key_checking": "no"}),))
330
305
  state = State(inventory, Config())
331
306
  connect_all(state)
332
307
 
333
- with patch("pyinfra.connectors.ssh.check_can_rsync"):
334
- add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
308
+ add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
335
309
 
336
310
  assert len(state.get_op_order()) == 1
337
311
 
@@ -349,6 +323,7 @@ class TestOperationsApi(PatchSSHTestCase):
349
323
  print_prefix=inventory.get_host("somehost").print_prefix,
350
324
  )
351
325
 
326
+ @patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
352
327
  def test_rsync_op_with_strict_host_key_checking_disabled_and_custom_config_file(self):
353
328
  inventory = make_inventory(
354
329
  hosts=(
@@ -364,8 +339,7 @@ class TestOperationsApi(PatchSSHTestCase):
364
339
  state = State(inventory, Config())
365
340
  connect_all(state)
366
341
 
367
- with patch("pyinfra.connectors.ssh.check_can_rsync"):
368
- add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
342
+ add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
369
343
 
370
344
  assert len(state.get_op_order()) == 1
371
345
 
@@ -384,6 +358,7 @@ class TestOperationsApi(PatchSSHTestCase):
384
358
  print_prefix=inventory.get_host("somehost").print_prefix,
385
359
  )
386
360
 
361
+ @patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
387
362
  def test_rsync_op_with_sanitized_custom_config_file(self):
388
363
  inventory = make_inventory(
389
364
  hosts=(("somehost", {"ssh_config_file": "/home/me/ssh_test_config && echo hi"}),)
@@ -391,8 +366,7 @@ class TestOperationsApi(PatchSSHTestCase):
391
366
  state = State(inventory, Config())
392
367
  connect_all(state)
393
368
 
394
- with patch("pyinfra.connectors.ssh.check_can_rsync"):
395
- add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
369
+ add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
396
370
 
397
371
  assert len(state.get_op_order()) == 1
398
372
 
@@ -403,7 +377,8 @@ class TestOperationsApi(PatchSSHTestCase):
403
377
  fake_run_local_process.assert_called_with(
404
378
  (
405
379
  "rsync -ax --delete --rsh "
406
- "\"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'\""
407
382
  " --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
408
383
  ),
409
384
  print_output=False,
@@ -415,7 +390,7 @@ class TestOperationsApi(PatchSSHTestCase):
415
390
  state = State(inventory, Config())
416
391
  connect_all(state)
417
392
 
418
- with patch("pyinfra.connectors.ssh.find_executable", lambda x: None):
393
+ with patch("pyinfra.connectors.ssh.which", lambda x: None):
419
394
  with self.assertRaises(OperationError) as context:
420
395
  add_op(state, files.rsync, "src", "dest")
421
396
 
@@ -430,14 +405,16 @@ class TestOperationsApi(PatchSSHTestCase):
430
405
  def setdefault(self, key, _):
431
406
  return self[key]
432
407
 
433
- state.op_meta = NoSetDefaultDict(lambda: {"serial": True})
408
+ op_meta_item = StateOperationMeta(tuple())
409
+ op_meta_item.global_arguments = {"_serial": True}
410
+ state.op_meta = NoSetDefaultDict(lambda: op_meta_item)
434
411
 
435
412
  connect_all(state)
436
413
 
437
414
  with self.assertRaises(OperationValueError) as context:
438
- add_op(state, files.file, "/var/log/pyinfra.log", serial=False)
415
+ add_op(state, files.file, "/var/log/pyinfra.log", _serial=False)
439
416
 
440
- assert context.exception.args[0] == "Cannot have different values for `serial`."
417
+ assert context.exception.args[0] == "Cannot have different values for `_serial`."
441
418
 
442
419
 
443
420
  class TestNestedOperationsApi(PatchSSHTestCase):
@@ -456,11 +433,11 @@ class TestNestedOperationsApi(PatchSSHTestCase):
456
433
 
457
434
  try:
458
435
  outer_result = server.shell(commands="echo outer")
459
- assert outer_result.combined_output_lines is None
436
+ assert outer_result._combined_output is None
460
437
 
461
438
  def callback():
462
439
  inner_result = server.shell(commands="echo inner")
463
- assert inner_result.combined_output_lines is not None
440
+ assert inner_result._combined_output is not None
464
441
 
465
442
  python.call(function=callback)
466
443
 
@@ -469,8 +446,8 @@ class TestNestedOperationsApi(PatchSSHTestCase):
469
446
  run_ops(state)
470
447
 
471
448
  assert len(state.get_op_order()) == 3
472
- assert state.results[somehost]["success_ops"] == 3
473
- assert outer_result.combined_output_lines is not None
449
+ assert state.results[somehost].success_ops == 3
450
+ assert outer_result._combined_output is not None
474
451
 
475
452
  disconnect_all(state)
476
453
  finally:
@@ -485,7 +462,7 @@ class TestOperationFailures(PatchSSHTestCase):
485
462
 
486
463
  add_op(state, server.shell, 'echo "hi"')
487
464
 
488
- with patch("pyinfra.connectors.ssh.run_shell_command") as fake_run_command:
465
+ with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
489
466
  fake_channel = FakeChannel(1)
490
467
  fake_run_command.return_value = (
491
468
  False,
@@ -500,18 +477,18 @@ class TestOperationFailures(PatchSSHTestCase):
500
477
  somehost = inventory.get_host("somehost")
501
478
 
502
479
  # Ensure the op was not flagged as success
503
- assert state.results[somehost]["success_ops"] == 0
480
+ assert state.results[somehost].success_ops == 0
504
481
  # And was flagged asn an error
505
- assert state.results[somehost]["error_ops"] == 1
482
+ assert state.results[somehost].error_ops == 1
506
483
 
507
484
  def test_ignore_errors_op_fail(self):
508
485
  inventory = make_inventory()
509
486
  state = State(inventory, Config())
510
487
  connect_all(state)
511
488
 
512
- add_op(state, server.shell, 'echo "hi"', ignore_errors=True)
489
+ add_op(state, server.shell, 'echo "hi"', _ignore_errors=True)
513
490
 
514
- with patch("pyinfra.connectors.ssh.run_shell_command") as fake_run_command:
491
+ with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
515
492
  fake_channel = FakeChannel(1)
516
493
  fake_run_command.return_value = (
517
494
  False,
@@ -524,25 +501,10 @@ class TestOperationFailures(PatchSSHTestCase):
524
501
  somehost = inventory.get_host("somehost")
525
502
 
526
503
  # Ensure the op was added to results
527
- assert state.results[somehost]["ops"] == 1
528
- assert state.results[somehost]["ignored_error_ops"] == 1
504
+ assert state.results[somehost].ops == 1
505
+ assert state.results[somehost].ignored_error_ops == 1
529
506
  # But not as a success
530
- assert state.results[somehost]["success_ops"] == 0
531
-
532
- # def test_no_invalid_op_call(self):
533
- # inventory = make_inventory()
534
- # state = State(inventory, Config())
535
- # connect_all(state)
536
- # ctx_state.set(state)
537
-
538
- # state.in_op = True
539
- # with self.assertRaises(PyinfraError):
540
- # server.user('user')
541
-
542
- # state.in_op = False
543
- # state.in_deploy = True
544
- # with self.assertRaises(PyinfraError):
545
- # server.user('user')
507
+ assert state.results[somehost].success_ops == 0
546
508
 
547
509
 
548
510
  class TestOperationOrdering(PatchSSHTestCase):
@@ -567,18 +529,19 @@ class TestOperationOrdering(PatchSSHTestCase):
567
529
  # Add op to just the second host - using the context modules such that
568
530
  # it replicates a deploy file.
569
531
  ctx_host.set(inventory.get_host("anotherhost"))
570
- first_context_hash = server.user("anotherhost_user").hash
532
+ first_context_hash = server.user("anotherhost_user")._hash
571
533
 
572
534
  # Add op to just the first host - using the context modules such that
573
535
  # it replicates a deploy file.
574
536
  ctx_host.set(inventory.get_host("somehost"))
575
- second_context_hash = server.user("somehost_user").hash
537
+ second_context_hash = server.user("somehost_user")._hash
576
538
 
577
539
  ctx_state.reset()
578
540
  ctx_host.reset()
579
541
 
580
542
  pyinfra.is_cli = False
581
543
 
544
+ print(state.ops)
582
545
  # Ensure there are two ops
583
546
  op_order = state.get_op_order()
584
547
  assert len(op_order) == 3
@@ -602,9 +565,9 @@ class TestOperationOrdering(PatchSSHTestCase):
602
565
  another_host = inventory.get_host("anotherhost")
603
566
 
604
567
  def add_another_op():
605
- 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
606
569
 
607
- 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
608
571
  second_op_hash = add_another_op() # note `add_op` will be called on an earlier line
609
572
 
610
573
  op_order = state.get_op_order()
@@ -615,66 +578,3 @@ class TestOperationOrdering(PatchSSHTestCase):
615
578
 
616
579
 
617
580
  this_filename = path.join("tests", "test_api", "test_api_operations.py")
618
-
619
-
620
- # class TestOperationExceptions(TestCase):
621
- # def test_add_op_rejects_cli(self):
622
- # pyinfra.is_cli = True
623
-
624
- # with self.assertRaises(PyinfraError) as context:
625
- # add_op(None, server.shell)
626
- # call_line = getframeinfo(currentframe()).lineno - 1
627
-
628
- # pyinfra.is_cli = False
629
-
630
- # assert context.exception.args[0] == (
631
- # '`add_op` should not be called when pyinfra is executing in CLI mode! '
632
- # '(line {0} in {1})'.format(call_line, this_filename)
633
- # )
634
-
635
- # def test_op_call_rejects_no_cli(self):
636
- # with self.assertRaises(PyinfraError) as context:
637
- # server.shell()
638
- # call_line = getframeinfo(currentframe()).lineno - 1
639
-
640
- # assert context.exception.args[0] == (
641
- # 'API operation called without state/host: '
642
- # 'server.shell (line {0} in {1})'.format(call_line, this_filename)
643
- # )
644
-
645
- # def test_op_call_rejects_in_op(self):
646
- # state = FakeState()
647
-
648
- # pyinfra.is_cli = True
649
- # ctx_state.set(state)
650
-
651
- # with self.assertRaises(PyinfraError) as context:
652
- # server.shell()
653
- # call_line = getframeinfo(currentframe()).lineno - 1
654
-
655
- # pyinfra.is_cli = False
656
- # ctx_state.reset()
657
-
658
- # assert context.exception.args[0] == (
659
- # 'Nested operation called without state/host: '
660
- # 'server.shell (line {0} in {1})'.format(call_line, this_filename)
661
- # )
662
-
663
- # def test_op_call_rejects_in_deploy(self):
664
- # state = FakeState()
665
- # state.in_op = False
666
-
667
- # pyinfra.is_cli = True
668
- # ctx_state.set(state)
669
-
670
- # with self.assertRaises(PyinfraError) as context:
671
- # server.shell()
672
- # call_line = getframeinfo(currentframe()).lineno - 1
673
-
674
- # pyinfra.is_cli = False
675
- # ctx_state.reset()
676
-
677
- # assert context.exception.args[0] == (
678
- # 'Nested deploy operation called without state/host: '
679
- # 'server.shell (line {0} in {1})'.format(call_line, this_filename)
680
- # )