pyinfra 3.3__py2.py3-none-any.whl → 3.4__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 (47) hide show
  1. pyinfra/api/arguments.py +8 -16
  2. pyinfra/api/deploy.py +1 -1
  3. pyinfra/api/facts.py +10 -26
  4. pyinfra/api/host.py +10 -4
  5. pyinfra/api/inventory.py +5 -2
  6. pyinfra/api/operation.py +1 -1
  7. pyinfra/api/util.py +20 -6
  8. pyinfra/connectors/docker.py +117 -38
  9. pyinfra/connectors/dockerssh.py +1 -0
  10. pyinfra/connectors/local.py +1 -0
  11. pyinfra/connectors/ssh.py +1 -0
  12. pyinfra/connectors/sshuserclient/client.py +5 -5
  13. pyinfra/connectors/terraform.py +3 -0
  14. pyinfra/connectors/vagrant.py +3 -0
  15. pyinfra/context.py +14 -5
  16. pyinfra/facts/brew.py +1 -0
  17. pyinfra/facts/docker.py +6 -2
  18. pyinfra/facts/git.py +10 -0
  19. pyinfra/facts/hardware.py +1 -1
  20. pyinfra/facts/opkg.py +1 -0
  21. pyinfra/facts/server.py +81 -23
  22. pyinfra/facts/systemd.py +1 -1
  23. pyinfra/operations/crontab.py +7 -5
  24. pyinfra/operations/docker.py +2 -0
  25. pyinfra/operations/files.py +64 -21
  26. pyinfra/operations/flatpak.py +17 -2
  27. pyinfra/operations/git.py +6 -2
  28. pyinfra/operations/server.py +34 -24
  29. pyinfra/operations/util/docker.py +4 -0
  30. pyinfra/operations/util/files.py +44 -3
  31. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/METADATA +5 -4
  32. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/RECORD +47 -47
  33. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/entry_points.txt +1 -0
  34. pyinfra_cli/inventory.py +1 -1
  35. pyinfra_cli/main.py +4 -2
  36. tests/test_api/test_api_arguments.py +25 -20
  37. tests/test_api/test_api_facts.py +28 -15
  38. tests/test_api/test_api_operations.py +43 -44
  39. tests/test_cli/test_cli.py +17 -17
  40. tests/test_cli/test_cli_inventory.py +4 -4
  41. tests/test_cli/test_context_objects.py +26 -26
  42. tests/test_connectors/test_docker.py +83 -43
  43. tests/test_connectors/test_ssh.py +153 -132
  44. tests/test_connectors/test_sshuserclient.py +10 -5
  45. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/LICENSE.md +0 -0
  46. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/WHEEL +0 -0
  47. {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,13 @@
1
+ from typing import cast
1
2
  from unittest.mock import MagicMock, patch
2
3
 
3
4
  from pyinfra.api import Config, State
4
- from pyinfra.api.arguments import CONNECTOR_ARGUMENT_KEYS, pop_global_arguments
5
+ from pyinfra.api.arguments import CONNECTOR_ARGUMENT_KEYS, AllArguments, pop_global_arguments
5
6
  from pyinfra.api.connect import connect_all
6
7
  from pyinfra.api.exceptions import PyinfraError
7
8
  from pyinfra.api.facts import get_facts
8
9
  from pyinfra.connectors.util import CommandOutput, OutputLine
10
+ from pyinfra.context import ctx_host, ctx_state
9
11
  from pyinfra.facts.server import Arch, Command
10
12
 
11
13
  from ..paramiko_util import PatchSSHTestCase
@@ -13,7 +15,9 @@ from ..util import make_inventory
13
15
 
14
16
 
15
17
  def _get_executor_defaults(state, host):
16
- global_argument_defaults, _ = pop_global_arguments({}, state=state, host=host)
18
+ with ctx_state.use(state):
19
+ with ctx_host.use(host):
20
+ global_argument_defaults, _ = pop_global_arguments(state, host, {})
17
21
  return {
18
22
  key: value
19
23
  for key, value in global_argument_defaults.items()
@@ -52,13 +56,16 @@ class TestFactsApi(PatchSSHTestCase):
52
56
  anotherhost = inventory.get_host("anotherhost")
53
57
 
54
58
  connect_all(state)
55
- anotherhost.current_op_global_arguments = {
56
- "_sudo": True,
57
- "_sudo_user": "someuser",
58
- "_su_user": "someuser",
59
- "_timeout": 10,
60
- "_env": {"HELLO": "WORLD"},
61
- }
59
+ anotherhost.current_op_global_arguments = cast(
60
+ AllArguments,
61
+ {
62
+ "_sudo": True,
63
+ "_sudo_user": "someuser",
64
+ "_su_user": "someuser",
65
+ "_timeout": 10,
66
+ "_env": {"HELLO": "WORLD"},
67
+ },
68
+ )
62
69
 
63
70
  with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
64
71
  fake_run_command.return_value = True, CommandOutput(
@@ -109,9 +116,12 @@ class TestFactsApi(PatchSSHTestCase):
109
116
 
110
117
  connect_all(state)
111
118
  anotherhost.in_op = True
112
- anotherhost.current_op_global_arguments = {
113
- "_ignore_errors": True,
114
- }
119
+ anotherhost.current_op_global_arguments = cast(
120
+ AllArguments,
121
+ {
122
+ "_ignore_errors": True,
123
+ },
124
+ )
115
125
 
116
126
  with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
117
127
  fake_run_command.return_value = False, MagicMock()
@@ -188,9 +198,12 @@ class TestFactsApi(PatchSSHTestCase):
188
198
  anotherhost.data._sudo_user = "this-should-be-overridden"
189
199
  anotherhost.data._su_user = "this-should-be-overridden"
190
200
 
191
- anotherhost.current_op_global_arguments = {
192
- "_su_user": "override-su-user",
193
- }
201
+ anotherhost.current_op_global_arguments = cast(
202
+ AllArguments,
203
+ {
204
+ "_su_user": "override-su-user",
205
+ },
206
+ )
194
207
 
195
208
  connect_all(state)
196
209
 
@@ -426,32 +426,31 @@ class TestNestedOperationsApi(PatchSSHTestCase):
426
426
 
427
427
  somehost = inventory.get_host("somehost")
428
428
 
429
- ctx_state.set(state)
430
- ctx_host.set(somehost)
431
-
432
429
  pyinfra.is_cli = True
433
430
 
434
- try:
435
- outer_result = server.shell(commands="echo outer")
436
- assert outer_result._combined_output is None
431
+ with ctx_state.use(state):
432
+ with ctx_host.use(somehost):
433
+ try:
434
+ outer_result = server.shell(commands="echo outer")
435
+ assert outer_result._combined_output is None
437
436
 
438
- def callback():
439
- inner_result = server.shell(commands="echo inner")
440
- assert inner_result._combined_output is not None
437
+ def callback():
438
+ inner_result = server.shell(commands="echo inner")
439
+ assert inner_result._combined_output is not None
441
440
 
442
- python.call(function=callback)
441
+ python.call(function=callback)
443
442
 
444
- assert len(state.get_op_order()) == 2
443
+ assert len(state.get_op_order()) == 2
445
444
 
446
- run_ops(state)
445
+ run_ops(state)
447
446
 
448
- assert len(state.get_op_order()) == 3
449
- assert state.results[somehost].success_ops == 3
450
- assert outer_result._combined_output is not None
447
+ assert len(state.get_op_order()) == 3
448
+ assert state.results[somehost].success_ops == 3
449
+ assert outer_result._combined_output is not None
451
450
 
452
- disconnect_all(state)
453
- finally:
454
- pyinfra.is_cli = False
451
+ disconnect_all(state)
452
+ finally:
453
+ pyinfra.is_cli = False
455
454
 
456
455
 
457
456
  class TestOperationFailures(PatchSSHTestCase):
@@ -519,40 +518,40 @@ class TestOperationOrdering(PatchSSHTestCase):
519
518
  state.current_deploy_filename = __file__
520
519
 
521
520
  pyinfra.is_cli = True
522
- ctx_state.set(state)
523
521
 
524
- # Add op to both hosts
525
- for name in ("anotherhost", "somehost"):
526
- ctx_host.set(inventory.get_host(name))
527
- server.shell("echo hi") # note this is called twice but on *the same line*
522
+ with ctx_state.use(state):
523
+ # Add op to both hosts
524
+ for name in ("anotherhost", "somehost"):
525
+ with ctx_host.use(inventory.get_host(name)):
526
+ server.shell("echo hi") # note this is called twice but on *the same line*
528
527
 
529
- # Add op to just the second host - using the context modules such that
530
- # it replicates a deploy file.
531
- ctx_host.set(inventory.get_host("anotherhost"))
532
- first_context_hash = server.user("anotherhost_user")._hash
528
+ # Add op to just the second host - using the context modules such that
529
+ # it replicates a deploy file.
530
+ ctx_host.set(inventory.get_host("anotherhost"))
531
+ first_context_hash = server.user("anotherhost_user")._hash
533
532
 
534
- # Add op to just the first host - using the context modules such that
535
- # it replicates a deploy file.
536
- ctx_host.set(inventory.get_host("somehost"))
537
- second_context_hash = server.user("somehost_user")._hash
533
+ # Add op to just the first host - using the context modules such that
534
+ # it replicates a deploy file.
535
+ ctx_host.set(inventory.get_host("somehost"))
536
+ second_context_hash = server.user("somehost_user")._hash
538
537
 
539
- ctx_state.reset()
540
- ctx_host.reset()
538
+ ctx_state.reset()
539
+ ctx_host.reset()
541
540
 
542
- pyinfra.is_cli = False
541
+ pyinfra.is_cli = False
543
542
 
544
- print(state.ops)
545
- # Ensure there are two ops
546
- op_order = state.get_op_order()
547
- assert len(op_order) == 3
543
+ print(state.ops)
544
+ # Ensure there are two ops
545
+ op_order = state.get_op_order()
546
+ assert len(op_order) == 3
548
547
 
549
- # And that the two ops above were called in the expected order
550
- assert op_order[1] == first_context_hash
551
- assert op_order[2] == second_context_hash
548
+ # And that the two ops above were called in the expected order
549
+ assert op_order[1] == first_context_hash
550
+ assert op_order[2] == second_context_hash
552
551
 
553
- # Ensure somehost has two ops and anotherhost only has the one
554
- assert len(state.ops[inventory.get_host("somehost")]) == 2
555
- assert len(state.ops[inventory.get_host("anotherhost")]) == 2
552
+ # Ensure somehost has two ops and anotherhost only has the one
553
+ assert len(state.ops[inventory.get_host("somehost")]) == 2
554
+ assert len(state.ops[inventory.get_host("anotherhost")]) == 2
556
555
 
557
556
  # In API mode, pyinfra *overrides* the line numbers such that whenever an
558
557
  # operation or deploy is added it is simply appended. This makes sense as
@@ -10,10 +10,10 @@ from .util import run_cli
10
10
  class TestCliEagerFlags(TestCase):
11
11
  def test_print_help(self):
12
12
  result = run_cli("--version")
13
- assert result.exit_code == 0, result.stdout
13
+ assert result.exit_code == 0, result.stderr
14
14
 
15
15
  result = run_cli("--help")
16
- assert result.exit_code == 0, result.stdout
16
+ assert result.exit_code == 0, result.stderr
17
17
 
18
18
 
19
19
  class TestOperationCli(PatchSSHTestCase):
@@ -22,7 +22,7 @@ class TestOperationCli(PatchSSHTestCase):
22
22
  path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
23
23
  "not_a_module.shell",
24
24
  )
25
- assert result.exit_code == 1, result.stdout
25
+ assert result.exit_code == 1, result.stderr
26
26
  assert "No such module: not_a_module"
27
27
 
28
28
  def test_invalid_operation_function(self):
@@ -30,7 +30,7 @@ class TestOperationCli(PatchSSHTestCase):
30
30
  path.join("tests", "test_cli", "deploy", "inventories", "inventory.py"),
31
31
  "server.not_an_operation",
32
32
  )
33
- assert result.exit_code == 1, result.stdout
33
+ assert result.exit_code == 1, result.stderr
34
34
  assert "No such operation: server.not_an_operation"
35
35
 
36
36
  def test_deploy_operation(self):
@@ -40,7 +40,7 @@ class TestOperationCli(PatchSSHTestCase):
40
40
  "server.shell",
41
41
  "echo hi",
42
42
  )
43
- assert result.exit_code == 0, result.stdout
43
+ assert result.exit_code == 0, result.stderr
44
44
 
45
45
  def test_deploy_operation_with_all(self):
46
46
  result = run_cli(
@@ -49,7 +49,7 @@ class TestOperationCli(PatchSSHTestCase):
49
49
  "server.shell",
50
50
  "echo hi",
51
51
  )
52
- assert result.exit_code == 0, result.stdout
52
+ assert result.exit_code == 0, result.stderr
53
53
 
54
54
  def test_deploy_operation_json_args(self):
55
55
  result = run_cli(
@@ -58,7 +58,7 @@ class TestOperationCli(PatchSSHTestCase):
58
58
  "server.shell",
59
59
  '[["echo hi"], {}]',
60
60
  )
61
- assert result.exit_code == 0, result.stdout
61
+ assert result.exit_code == 0, result.stderr
62
62
 
63
63
 
64
64
  class TestFactCli(PatchSSHTestCase):
@@ -68,8 +68,8 @@ class TestFactCli(PatchSSHTestCase):
68
68
  "fact",
69
69
  "server.Os",
70
70
  )
71
- assert result.exit_code == 0, result.stdout
72
- assert '"somehost": null' in result.stdout
71
+ assert result.exit_code == 0, result.stderr
72
+ assert '"somehost": null' in result.stderr
73
73
 
74
74
  def test_get_fact_with_kwargs(self):
75
75
  result = run_cli(
@@ -78,8 +78,8 @@ class TestFactCli(PatchSSHTestCase):
78
78
  "files.File",
79
79
  "path=.",
80
80
  )
81
- assert result.exit_code == 0, result.stdout
82
- assert '"somehost": null' in result.stdout
81
+ assert result.exit_code == 0, result.stderr
82
+ assert '"somehost": null' in result.stderr
83
83
 
84
84
 
85
85
  class TestExecCli(PatchSSHTestCase):
@@ -90,7 +90,7 @@ class TestExecCli(PatchSSHTestCase):
90
90
  "--",
91
91
  "echo hi",
92
92
  )
93
- assert result.exit_code == 0, result.stdout
93
+ assert result.exit_code == 0, result.stderr
94
94
 
95
95
  def test_exec_command_with_options(self):
96
96
  result = run_cli(
@@ -108,7 +108,7 @@ class TestExecCli(PatchSSHTestCase):
108
108
  "--",
109
109
  "echo hi",
110
110
  )
111
- assert result.exit_code == 0, result.stdout
111
+ assert result.exit_code == 0, result.stderr
112
112
 
113
113
  def test_exec_command_with_serial(self):
114
114
  result = run_cli(
@@ -118,7 +118,7 @@ class TestExecCli(PatchSSHTestCase):
118
118
  "--",
119
119
  "echo hi",
120
120
  )
121
- assert result.exit_code == 0, result.stdout
121
+ assert result.exit_code == 0, result.stderr
122
122
 
123
123
  def test_exec_command_with_no_wait(self):
124
124
  result = run_cli(
@@ -128,7 +128,7 @@ class TestExecCli(PatchSSHTestCase):
128
128
  "--",
129
129
  "echo hi",
130
130
  )
131
- assert result.exit_code == 0, result.stdout
131
+ assert result.exit_code == 0, result.stderr
132
132
 
133
133
  def test_exec_command_with_debug_operations(self):
134
134
  result = run_cli(
@@ -138,7 +138,7 @@ class TestExecCli(PatchSSHTestCase):
138
138
  "--",
139
139
  "echo hi",
140
140
  )
141
- assert result.exit_code == 0, result.stdout
141
+ assert result.exit_code == 0, result.stderr
142
142
 
143
143
  def test_exec_command_with_debug_facts(self):
144
144
  result = run_cli(
@@ -148,7 +148,7 @@ class TestExecCli(PatchSSHTestCase):
148
148
  "--",
149
149
  "echo hi",
150
150
  )
151
- assert result.exit_code == 0, result.stdout
151
+ assert result.exit_code == 0, result.stderr
152
152
 
153
153
 
154
154
  class TestDirectMainExecution(PatchSSHTestCase):
@@ -80,7 +80,7 @@ class TestCliInventory(PatchSSHTestCase):
80
80
  assert result.exit_code == 0, result.stdout
81
81
  assert (
82
82
  'Ignoring variable "_hosts" in inventory file since it starts with a leading underscore'
83
- in result.stdout
83
+ in result.stderr
84
84
  )
85
85
  assert inventory.hosts == {}
86
86
 
@@ -97,9 +97,9 @@ class TestCliInventory(PatchSSHTestCase):
97
97
  )
98
98
 
99
99
  assert result.exit_code == 0, result.stdout
100
- assert 'Ignoring variable "dict_hosts" in inventory file' in result.stdout, result.stdout
100
+ assert 'Ignoring variable "dict_hosts" in inventory file' in result.stderr, result.stdout
101
101
  assert (
102
- 'Ignoring variable "generator_hosts" in inventory file' in result.stdout
102
+ 'Ignoring variable "generator_hosts" in inventory file' in result.stderr
103
103
  ), result.stdout
104
104
  assert inventory.hosts == {}
105
105
 
@@ -115,5 +115,5 @@ class TestCliInventory(PatchSSHTestCase):
115
115
  )
116
116
 
117
117
  assert result.exit_code == 0, result.stdout
118
- assert 'Ignoring host group "issue_662"' in result.stdout, result.stdout
118
+ assert 'Ignoring host group "issue_662"' in result.stderr, result.stdout
119
119
  assert inventory.hosts == {}
@@ -5,8 +5,8 @@ from pyinfra.api import Host, Inventory
5
5
  from pyinfra.context import ctx_host, ctx_inventory
6
6
 
7
7
 
8
- def _create_host():
9
- return Host(None, None, None, None)
8
+ def _create_host(name: str = "host"):
9
+ return Host(name, Inventory(([name], {})), None, None)
10
10
 
11
11
 
12
12
  class TestHostContextObject(TestCase):
@@ -15,44 +15,44 @@ class TestHostContextObject(TestCase):
15
15
 
16
16
  def test_context_host_eq(self):
17
17
  host_obj = _create_host()
18
- ctx_host.set(host_obj)
19
- assert host == host_obj
18
+ with ctx_host.use(host_obj):
19
+ assert host == host_obj
20
20
 
21
21
  def test_context_host_repr(self):
22
- host_obj = _create_host()
23
- ctx_host.set(host_obj)
24
- assert repr(host) == "ContextObject(Host):Host(None)"
22
+ host_obj = _create_host("somehost")
23
+ with ctx_host.use(host_obj):
24
+ assert repr(host) == "ContextObject(Host):Host(somehost)"
25
25
 
26
26
  def test_context_host_str(self):
27
27
  host_obj = _create_host()
28
- ctx_host.set(host_obj)
29
- assert str(host_obj) == "None"
28
+ with ctx_host.use(host_obj):
29
+ assert str(host_obj) == "host"
30
30
 
31
31
  def test_context_host_attr(self):
32
32
  host_obj = _create_host()
33
- ctx_host.set(host_obj)
34
33
 
35
34
  with self.assertRaises(AttributeError):
36
35
  host_obj.hello
37
36
 
38
- setattr(host, "hello", "world")
39
- assert host_obj.hello == host.hello
37
+ with ctx_host.use(host_obj):
38
+ setattr(host, "hello", "world")
39
+ assert host_obj.hello == host.hello
40
40
 
41
41
  def test_context_host_class_attr(self):
42
42
  host_obj = _create_host()
43
- ctx_host.set(host_obj)
44
- assert ctx_host.isset() is True
45
43
 
46
- with self.assertRaises(AttributeError):
47
- host_obj.hello
44
+ with ctx_host.use(host_obj):
45
+ assert ctx_host.isset() is True
46
+
47
+ with self.assertRaises(AttributeError):
48
+ host_obj.hello
48
49
 
49
- setattr(Host, "hello", "class_world")
50
- setattr(host_obj, "hello", "instance_world")
50
+ setattr(Host, "hello", "class_world")
51
+ setattr(host_obj, "hello", "instance_world")
51
52
 
52
- assert host.hello == host.hello
53
+ assert host.hello == host.hello
53
54
 
54
55
  # Reset and check fallback to class variable
55
- ctx_host.reset()
56
56
  assert ctx_host.isset() is False
57
57
  assert host.hello == "class_world"
58
58
 
@@ -60,14 +60,14 @@ class TestHostContextObject(TestCase):
60
60
  class TestInventoryContextObject(TestCase):
61
61
  def test_context_inventory_len(self):
62
62
  inventory_obj = Inventory(("host", "anotherhost"))
63
- ctx_inventory.set(inventory_obj)
64
- assert ctx_inventory.isset() is True
65
63
 
66
- assert len(inventory) == len(inventory_obj)
64
+ with ctx_inventory.use(inventory_obj):
65
+ assert ctx_inventory.isset() is True
66
+ assert len(inventory) == len(inventory_obj)
67
67
 
68
68
  def test_context_inventory_iter(self):
69
69
  inventory_obj = Inventory(("host", "anotherhost"))
70
- ctx_inventory.set(inventory_obj)
71
- assert ctx_inventory.isset() is True
72
70
 
73
- assert list(iter(inventory)) == list(iter(inventory_obj))
71
+ with ctx_inventory.use(inventory_obj):
72
+ assert ctx_inventory.isset() is True
73
+ assert list(iter(inventory)) == list(iter(inventory_obj))