pyinfra 2.9.1__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.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
  113. pyinfra-3.0.dist-info/RECORD +167 -0
  114. {pyinfra-2.9.1.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.1.dist-info/RECORD +0 -170
  151. pyinfra-2.9.1.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.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
  156. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
pyinfra/operations/gem.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Manage Ruby gem packages. (see https://rubygems.org/ )
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  from pyinfra import host
6
8
  from pyinfra.api import operation
7
9
  from pyinfra.facts.gem import GemPackages
@@ -9,8 +11,8 @@ from pyinfra.facts.gem import GemPackages
9
11
  from .util.packaging import ensure_packages
10
12
 
11
13
 
12
- @operation
13
- def packages(packages=None, present=True, latest=False):
14
+ @operation()
15
+ def packages(packages: str | list[str] | None = None, present=True, latest=False):
14
16
  """
15
17
  Add/remove/update gem packages.
16
18
 
pyinfra/operations/git.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Manage git repositories and configuration.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import re
6
8
 
7
9
  from pyinfra import host
@@ -13,10 +15,8 @@ from . import files, ssh
13
15
  from .util.files import chown, unix_path_join
14
16
 
15
17
 
16
- @operation(
17
- pipeline_facts={"git_config": "repo"},
18
- )
19
- def config(key, value, multi_value=False, repo=None):
18
+ @operation()
19
+ def config(key: str, value: str, multi_value=False, repo: str | None = None):
20
20
  """
21
21
  Manage git config for a repository or globally.
22
22
 
@@ -54,27 +54,23 @@ def config(key, value, multi_value=False, repo=None):
54
54
 
55
55
  if not multi_value and existing_config.get(key) != [value]:
56
56
  yield '{0} {1} "{2}"'.format(base_command, key, value)
57
- existing_config[key] = [value]
58
57
 
59
58
  elif multi_value and value not in existing_config.get(key, []):
60
59
  yield '{0} --add {1} "{2}"'.format(base_command, key, value)
61
- existing_config.setdefault(key, []).append(value)
62
60
 
63
61
  else:
64
62
  host.noop("git config {0} is set to {1}".format(key, value))
65
63
 
66
64
 
67
- @operation(
68
- pipeline_facts={"git_branch": "target"},
69
- )
65
+ @operation()
70
66
  def repo(
71
- src,
72
- dest,
73
- branch=None,
67
+ src: str,
68
+ dest: str,
69
+ branch: str | None = None,
74
70
  pull=True,
75
71
  rebase=False,
76
- user=None,
77
- group=None,
72
+ user: str | None = None,
73
+ group: str | None = None,
78
74
  ssh_keyscan=False,
79
75
  update_submodules=False,
80
76
  recursive_submodules=False,
@@ -105,7 +101,7 @@ def repo(
105
101
  """
106
102
 
107
103
  # Ensure our target directory exists
108
- yield from files.directory(dest)
104
+ yield from files.directory._inner(dest)
109
105
 
110
106
  # Do we need to scan for the remote host key?
111
107
  if ssh_keyscan:
@@ -113,7 +109,7 @@ def repo(
113
109
  domain = re.match(r"^[a-zA-Z0-9]+@([0-9a-zA-Z\.\-]+)", src)
114
110
 
115
111
  if domain:
116
- yield from ssh.keyscan(domain.group(1))
112
+ yield from ssh.keyscan._inner(domain.group(1))
117
113
  else:
118
114
  raise OperationError(
119
115
  "Could not parse domain (to SSH keyscan) from: {0}".format(src),
@@ -130,21 +126,11 @@ def repo(
130
126
  git_commands.append("clone {0} --branch {1} .".format(src, branch))
131
127
  else:
132
128
  git_commands.append("clone {0} .".format(src))
133
-
134
- host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
135
- host.create_fact(
136
- Directory,
137
- kwargs={"path": git_dir},
138
- data={"user": user, "group": group},
139
- )
140
-
141
129
  # Ensuring existing repo
142
130
  else:
143
131
  if branch and host.get_fact(GitBranch, repo=dest) != branch:
144
132
  git_commands.append("fetch") # fetch to ensure we have the branch locally
145
133
  git_commands.append("checkout {0}".format(branch))
146
- host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
147
-
148
134
  if pull:
149
135
  if rebase:
150
136
  git_commands.append("pull --rebase")
@@ -171,19 +157,19 @@ def repo(
171
157
 
172
158
  @operation()
173
159
  def worktree(
174
- worktree,
175
- repo=None,
160
+ worktree: str,
161
+ repo: str | None = None,
176
162
  detached=False,
177
- new_branch=None,
178
- commitish=None,
163
+ new_branch: str | None = None,
164
+ commitish: str | None = None,
179
165
  pull=True,
180
166
  rebase=False,
181
- from_remote_branch=None,
167
+ from_remote_branch: tuple[str, str] | None = None,
182
168
  present=True,
183
169
  assume_repo_exists=False,
184
170
  force=False,
185
- user=None,
186
- group=None,
171
+ user: str | None = None,
172
+ group: str | None = None,
187
173
  ):
188
174
  """
189
175
  Manage git worktrees.
@@ -293,7 +279,6 @@ def worktree(
293
279
 
294
280
  # Doesn't exist & we want it
295
281
  if not host.get_fact(Directory, path=worktree) and present:
296
-
297
282
  # be sure that `repo` is a GIT repository
298
283
  if not assume_repo_exists and not host.get_fact(
299
284
  Directory,
@@ -321,12 +306,8 @@ def worktree(
321
306
  if user or group:
322
307
  yield chown(worktree, user, group, recursive=True)
323
308
 
324
- host.create_fact(Directory, kwargs={"path": worktree}, data={"user": user, "group": group})
325
- host.create_fact(GitTrackingBranch, kwargs={"repo": worktree}, data=new_branch)
326
-
327
309
  # It exists and we don't want it
328
310
  elif host.get_fact(Directory, path=worktree) and not present:
329
-
330
311
  command = "cd {0} && git worktree remove .".format(worktree)
331
312
 
332
313
  if force:
@@ -334,12 +315,8 @@ def worktree(
334
315
 
335
316
  yield command
336
317
 
337
- host.delete_fact(Directory, kwargs={"path": worktree})
338
- host.create_fact(GitTrackingBranch, kwargs={"repo": worktree})
339
-
340
318
  # It exists and we still want it => pull/rebase it
341
319
  elif host.get_fact(Directory, path=worktree) and present:
342
-
343
320
  # pull the worktree only if it's already linked to a tracking branch or
344
321
  # if a remote branch is set
345
322
  if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
@@ -359,11 +336,11 @@ def worktree(
359
336
  yield command
360
337
 
361
338
 
362
- @operation
339
+ @operation()
363
340
  def bare_repo(
364
- path,
365
- user=None,
366
- group=None,
341
+ path: str,
342
+ user: str | None = None,
343
+ group: str | None = None,
367
344
  present=True,
368
345
  ):
369
346
  """
@@ -384,7 +361,7 @@ def bare_repo(
384
361
  )
385
362
  """
386
363
 
387
- yield from files.directory(path, present=present)
364
+ yield from files.directory._inner(path, present=present)
388
365
 
389
366
  if present:
390
367
  head_filename = unix_path_join(path, "HEAD")
@@ -397,9 +374,3 @@ def bare_repo(
397
374
  else:
398
375
  if (user and head_file["user"] != user) or (group and head_file["group"] != group):
399
376
  yield chown(path, user, group, recursive=True)
400
-
401
- host.create_fact(
402
- File,
403
- kwargs={"path": head_filename},
404
- data={"user": user, "group": group, "mode": None},
405
- )
@@ -2,18 +2,20 @@
2
2
  The iptables modules handles iptables rules
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  from pyinfra import host
6
8
  from pyinfra.api import operation
7
9
  from pyinfra.api.exceptions import OperationError
8
10
  from pyinfra.facts.iptables import Ip6tablesChains, Ip6tablesRules, IptablesChains, IptablesRules
9
11
 
10
12
 
11
- @operation
13
+ @operation()
12
14
  def chain(
13
- chain,
15
+ chain: str,
14
16
  present=True,
15
17
  table="filter",
16
- policy=None,
18
+ policy: str | None = None,
17
19
  version=4,
18
20
  ):
19
21
  """
@@ -41,7 +43,6 @@ def chain(
41
43
  if not present:
42
44
  if chain in chains:
43
45
  yield "{0} -X {1}".format(command, chain)
44
- chains.pop(chain)
45
46
  else:
46
47
  host.noop("iptables chain {0} does not exist".format(chain))
47
48
  return
@@ -49,44 +50,42 @@ def chain(
49
50
  if present:
50
51
  if chain not in chains:
51
52
  yield "{0} -N {1}".format(command, chain)
52
- chains[chain] = None # policy will be set below
53
53
  else:
54
54
  host.noop("iptables chain {0} exists".format(chain))
55
55
 
56
56
  if policy:
57
57
  if chain not in chains or chains[chain] != policy:
58
58
  yield "{0} -P {1} {2}".format(command, chain, policy)
59
- chains[chain] = policy
60
59
 
61
60
 
62
- @operation
61
+ @operation()
63
62
  def rule(
64
- chain,
65
- jump,
66
- present=True,
67
- table="filter",
68
- append=True,
69
- version=4,
63
+ chain: str,
64
+ jump: str,
65
+ present: bool = True,
66
+ table: str = "filter",
67
+ append: bool = True,
68
+ version: int = 4,
70
69
  # Core iptables filter arguments
71
- protocol=None,
72
- not_protocol=None,
73
- source=None,
74
- not_source=None,
75
- destination=None,
76
- not_destination=None,
77
- in_interface=None,
78
- not_in_interface=None,
79
- out_interface=None,
80
- not_out_interface=None,
70
+ protocol: str | None = None,
71
+ not_protocol: str | None = None,
72
+ source: str | None = None,
73
+ not_source: str | None = None,
74
+ destination: str | None = None,
75
+ not_destination: str | None = None,
76
+ in_interface: str | None = None,
77
+ not_in_interface: str | None = None,
78
+ out_interface: str | None = None,
79
+ not_out_interface: str | None = None,
81
80
  # After-rule arguments
82
- to_destination=None,
83
- to_source=None,
84
- to_ports=None,
85
- log_prefix=None,
81
+ to_destination: str | None = None,
82
+ to_source: str | None = None,
83
+ to_ports: int | str | None = None,
84
+ log_prefix: str | None = None,
86
85
  # Extras and extra shortcuts
87
- destination_port=None,
88
- source_port=None,
89
- extras="",
86
+ destination_port: int | None = None,
87
+ source_port: int | None = None,
88
+ extras: str = "",
90
89
  ):
91
90
  """
92
91
  Add/remove iptables rules.
@@ -310,8 +309,3 @@ def rule(
310
309
 
311
310
  # Build the final iptables command
312
311
  yield " ".join(args)
313
-
314
- if action == "-D":
315
- rules.remove(definition)
316
- else:
317
- rules.append(definition)
@@ -2,6 +2,8 @@
2
2
  Manage launchd services.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  from pyinfra import host
6
8
  from pyinfra.api import operation
7
9
  from pyinfra.facts.launchd import LaunchdStatus
@@ -9,12 +11,12 @@ from pyinfra.facts.launchd import LaunchdStatus
9
11
  from .util.service import handle_service_control
10
12
 
11
13
 
12
- @operation
14
+ @operation()
13
15
  def service(
14
- service,
16
+ service: str,
15
17
  running=True,
16
18
  restarted=False,
17
- command=None,
19
+ command: str | None = None,
18
20
  ):
19
21
  """
20
22
  Manage the state of systemd managed services.
@@ -33,11 +35,8 @@ def service(
33
35
  service,
34
36
  host.get_fact(LaunchdStatus),
35
37
  "launchctl {1} {0}",
36
- # No support for restart/reload/command
37
38
  running,
38
- None,
39
- None,
40
- None,
39
+ # No support for restart/reload/command
41
40
  )
42
41
 
43
42
  # No restart command, so just stop/start
pyinfra/operations/lxd.py CHANGED
@@ -2,22 +2,25 @@
2
2
  The LXD modules manage LXD containers
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
5
9
  from pyinfra import host
6
10
  from pyinfra.api import operation
7
11
  from pyinfra.facts.lxd import LxdContainers
8
12
 
9
13
 
10
- def get_container_named(name, containers):
14
+ def get_container_named(name: str, containers: list[dict[str, Any]]) -> dict[str, Any] | None:
11
15
  for container in containers:
12
16
  if container["name"] == name:
13
17
  return container
14
- else:
15
- return None
18
+ return None
16
19
 
17
20
 
18
- @operation
21
+ @operation()
19
22
  def container(
20
- id,
23
+ id: str,
21
24
  present=True,
22
25
  image="ubuntu:16.04",
23
26
  ):
@@ -53,8 +56,6 @@ def container(
53
56
 
54
57
  # Command to remove the container:
55
58
  yield "lxc delete {0}".format(id)
56
-
57
- current_containers.remove(container)
58
59
  else:
59
60
  host.noop("container {0} does not exist".format(id))
60
61
 
@@ -63,11 +64,5 @@ def container(
63
64
  if not container:
64
65
  # Command to create the container:
65
66
  yield "lxc launch {image} {id} < /dev/null".format(id=id, image=image)
66
- current_containers.append(
67
- {
68
- "name": id,
69
- "image": image,
70
- },
71
- )
72
67
  else:
73
68
  host.noop("container {0} exists".format(id))
@@ -13,6 +13,8 @@ See the example/mysql.py
13
13
 
14
14
  """
15
15
 
16
+ from __future__ import annotations
17
+
16
18
  from pyinfra import host
17
19
  from pyinfra.api import MaskString, OperationError, QuoteString, StringCommand, operation
18
20
  from pyinfra.facts.mysql import (
@@ -26,13 +28,13 @@ from pyinfra.facts.mysql import (
26
28
 
27
29
  @operation(is_idempotent=False)
28
30
  def sql(
29
- sql,
30
- database=None,
31
+ sql: str,
32
+ database: str | None = None,
31
33
  # Details for speaking to MySQL via `mysql` CLI
32
- mysql_user=None,
33
- mysql_password=None,
34
- mysql_host=None,
35
- mysql_port=None,
34
+ mysql_user: str | None = None,
35
+ mysql_password: str | None = None,
36
+ mysql_host: str | None = None,
37
+ mysql_port: int | None = None,
36
38
  ):
37
39
  """
38
40
  Execute arbitrary SQL against MySQL.
@@ -52,29 +54,30 @@ def sql(
52
54
  )
53
55
 
54
56
 
55
- @operation
57
+ @operation()
56
58
  def user(
57
- user,
58
- present=True,
59
- user_hostname="localhost",
60
- password=None,
61
- privileges=None,
59
+ user: str,
60
+ present: bool = True,
61
+ user_hostname: str = "localhost",
62
+ password: str | None = None,
63
+ privileges: str | list[str] | None = None,
62
64
  # MySQL REQUIRE SSL/TLS options
63
- require=None, # SSL or X509
64
- require_cipher=False,
65
- require_issuer=False,
66
- require_subject=False,
65
+ require: str | None = None, # SSL or X509
66
+ require_cipher: str | None = None,
67
+ require_issuer: str | None = None,
68
+ require_subject: str | None = None,
67
69
  # MySQL WITH resource limit options
68
- max_connections=None,
69
- max_queries_per_hour=None,
70
- max_updates_per_hour=None,
71
- max_connections_per_hour=None,
70
+ max_connections: int | None = None,
71
+ max_queries_per_hour: int | None = None,
72
+ max_updates_per_hour: int | None = None,
73
+ max_connections_per_hour: int | None = None,
72
74
  # Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
73
- mysql_user=None,
74
- mysql_password=None,
75
- mysql_host=None,
76
- mysql_port=None,
75
+ mysql_user: str | None = None,
76
+ mysql_password: str | None = None,
77
+ mysql_host: str | None = None,
78
+ mysql_port: int | None = None,
77
79
  ):
80
+ ...
78
81
  """
79
82
  Add/remove/update MySQL users.
80
83
 
@@ -162,22 +165,10 @@ def user(
162
165
  host=mysql_host,
163
166
  port=mysql_port,
164
167
  )
165
- current_users.pop(user_host)
166
168
  else:
167
169
  host.noop("mysql user {0}@{1} does not exist".format(user, user_hostname))
168
170
  return
169
171
 
170
- new_or_updated_user_fact = {
171
- "ssl_type": "ANY" if require == "SSL" else require,
172
- "ssl_cipher": require_cipher,
173
- "x509_issuer": require_issuer,
174
- "x509_subject": require_subject,
175
- "max_user_connections": max_connections,
176
- "max_questions": max_queries_per_hour,
177
- "max_updates": max_updates_per_hour,
178
- "max_connections": max_connections_per_hour,
179
- }
180
-
181
172
  if present and not is_present:
182
173
  sql_bits = ['CREATE USER "{0}"@"{1}"'.format(user, user_hostname)]
183
174
  if password:
@@ -224,8 +215,6 @@ def user(
224
215
  port=mysql_port,
225
216
  )
226
217
 
227
- current_users[user_host] = new_or_updated_user_fact
228
-
229
218
  if present and is_present:
230
219
  current_user = current_users.get(user_host)
231
220
 
@@ -277,14 +266,13 @@ def user(
277
266
  host=mysql_host,
278
267
  port=mysql_port,
279
268
  )
280
- current_user.update(new_or_updated_user_fact)
281
269
  else:
282
270
  host.noop("mysql user {0}@{1} exists".format(user, user_hostname))
283
271
 
284
272
  # If we're here either the user exists or we just created them; either way
285
273
  # now we can check any privileges are set.
286
274
  if privileges:
287
- yield from _privileges(
275
+ yield from _privileges._inner(
288
276
  user,
289
277
  privileges,
290
278
  user_hostname=user_hostname,
@@ -295,26 +283,27 @@ def user(
295
283
  )
296
284
 
297
285
 
298
- @operation
286
+ @operation()
299
287
  def database(
300
- database,
288
+ database: str,
301
289
  # Desired database settings
302
- present=True,
303
- collate=None,
304
- charset=None,
305
- user=None,
306
- user_hostname="localhost",
307
- user_privileges="ALL",
290
+ present: bool = True,
291
+ collate: str | None = None,
292
+ charset: str | None = None,
293
+ user: str | None = None,
294
+ user_hostname: str = "localhost",
295
+ user_privileges: str | list[str] = "ALL",
308
296
  # Details for speaking to MySQL via `mysql` CLI
309
- mysql_user=None,
310
- mysql_password=None,
311
- mysql_host=None,
312
- mysql_port=None,
297
+ mysql_user: str | None = None,
298
+ mysql_password: str | None = None,
299
+ mysql_host: str | None = None,
300
+ mysql_port: int | None = None,
313
301
  ):
302
+ ...
314
303
  """
315
304
  Add/remove MySQL databases.
316
305
 
317
- + name: the name of the database
306
+ + database: the name of the database
318
307
  + present: whether the database should exist or not
319
308
  + collate: the collate to use when creating the database
320
309
  + charset: the charset to use when creating the database
@@ -359,7 +348,6 @@ def database(
359
348
  host=mysql_host,
360
349
  port=mysql_port,
361
350
  )
362
- current_databases.pop(database)
363
351
  else:
364
352
  host.noop("mysql database {0} does not exist".format(database))
365
353
  return
@@ -381,16 +369,12 @@ def database(
381
369
  host=mysql_host,
382
370
  port=mysql_port,
383
371
  )
384
- current_databases[database] = {
385
- "collate": collate,
386
- "charset": charset,
387
- }
388
372
  else:
389
373
  host.noop("mysql database {0} exists".format(database))
390
374
 
391
375
  # Ensure any user privileges for this database
392
376
  if user and user_privileges:
393
- yield from privileges(
377
+ yield from privileges._inner(
394
378
  user,
395
379
  user_hostname=user_hostname,
396
380
  privileges=user_privileges,
@@ -402,20 +386,20 @@ def database(
402
386
  )
403
387
 
404
388
 
405
- @operation
389
+ @operation()
406
390
  def privileges(
407
- user,
408
- privileges,
391
+ user: str,
392
+ privileges: str | list[str] | set[str],
409
393
  user_hostname="localhost",
410
394
  database="*",
411
395
  table="*",
412
396
  flush=True,
413
397
  with_grant_option=False,
414
398
  # Details for speaking to MySQL via `mysql` CLI
415
- mysql_user=None,
416
- mysql_password=None,
417
- mysql_host=None,
418
- mysql_port=None,
399
+ mysql_user: str | None = None,
400
+ mysql_password: str | None = None,
401
+ mysql_host: str | None = None,
402
+ mysql_port: int | None = None,
419
403
  ):
420
404
  """
421
405
  Add/remove MySQL privileges for a user, either global, database or table specific.
@@ -504,9 +488,6 @@ def privileges(
504
488
  # Find / grant any privileges that we want but do not exist
505
489
  privileges_to_grant = privileges - existing_privileges
506
490
 
507
- user_grants[database_table] -= privileges_to_revoke
508
- user_grants[database_table].update(privileges_to_grant)
509
-
510
491
  if privileges_to_grant:
511
492
  # We will grant something on this table, no need to revoke "USAGE" as it will be overridden
512
493
  privileges_to_revoke.discard("USAGE")
@@ -547,13 +528,13 @@ _privileges = privileges # noqa: E305 (for use where kwarg is the same)
547
528
 
548
529
  @operation(is_idempotent=False)
549
530
  def dump(
550
- dest,
551
- database=None,
531
+ dest: str,
532
+ database: str | None = None,
552
533
  # Details for speaking to MySQL via `mysql` CLI
553
- mysql_user=None,
554
- mysql_password=None,
555
- mysql_host=None,
556
- mysql_port=None,
534
+ mysql_user: str | None = None,
535
+ mysql_password: str | None = None,
536
+ mysql_host: str | None = None,
537
+ mysql_port: int | None = None,
557
538
  ):
558
539
  """
559
540
  Dump a MySQL database into a ``.sql`` file. Requires ``mysqldump``.
@@ -588,13 +569,13 @@ def dump(
588
569
 
589
570
  @operation(is_idempotent=False)
590
571
  def load(
591
- src,
592
- database=None,
572
+ src: str,
573
+ database: str | None = None,
593
574
  # Details for speaking to MySQL via `mysql` CLI
594
- mysql_user=None,
595
- mysql_password=None,
596
- mysql_host=None,
597
- mysql_port=None,
575
+ mysql_user: str | None = None,
576
+ mysql_password: str | None = None,
577
+ mysql_host: str | None = None,
578
+ mysql_port: int | None = None,
598
579
  ):
599
580
  """
600
581
  Load ``.sql`` file into a database.