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/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
@@ -10,7 +12,7 @@ from .util.packaging import ensure_packages
10
12
 
11
13
 
12
14
  @operation()
13
- def packages(packages=None, present=True, latest=False):
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
 
@@ -62,17 +62,15 @@ def config(key, value, multi_value=False, repo=None):
62
62
  host.noop("git config {0} is set to {1}".format(key, value))
63
63
 
64
64
 
65
- @operation(
66
- pipeline_facts={"git_branch": "target"},
67
- )
65
+ @operation()
68
66
  def repo(
69
- src,
70
- dest,
71
- branch=None,
67
+ src: str,
68
+ dest: str,
69
+ branch: str | None = None,
72
70
  pull=True,
73
71
  rebase=False,
74
- user=None,
75
- group=None,
72
+ user: str | None = None,
73
+ group: str | None = None,
76
74
  ssh_keyscan=False,
77
75
  update_submodules=False,
78
76
  recursive_submodules=False,
@@ -103,7 +101,7 @@ def repo(
103
101
  """
104
102
 
105
103
  # Ensure our target directory exists
106
- yield from files.directory(dest)
104
+ yield from files.directory._inner(dest)
107
105
 
108
106
  # Do we need to scan for the remote host key?
109
107
  if ssh_keyscan:
@@ -111,7 +109,7 @@ def repo(
111
109
  domain = re.match(r"^[a-zA-Z0-9]+@([0-9a-zA-Z\.\-]+)", src)
112
110
 
113
111
  if domain:
114
- yield from ssh.keyscan(domain.group(1))
112
+ yield from ssh.keyscan._inner(domain.group(1))
115
113
  else:
116
114
  raise OperationError(
117
115
  "Could not parse domain (to SSH keyscan) from: {0}".format(src),
@@ -159,19 +157,19 @@ def repo(
159
157
 
160
158
  @operation()
161
159
  def worktree(
162
- worktree,
163
- repo=None,
160
+ worktree: str,
161
+ repo: str | None = None,
164
162
  detached=False,
165
- new_branch=None,
166
- commitish=None,
163
+ new_branch: str | None = None,
164
+ commitish: str | None = None,
167
165
  pull=True,
168
166
  rebase=False,
169
- from_remote_branch=None,
167
+ from_remote_branch: tuple[str, str] | None = None,
170
168
  present=True,
171
169
  assume_repo_exists=False,
172
170
  force=False,
173
- user=None,
174
- group=None,
171
+ user: str | None = None,
172
+ group: str | None = None,
175
173
  ):
176
174
  """
177
175
  Manage git worktrees.
@@ -340,9 +338,9 @@ def worktree(
340
338
 
341
339
  @operation()
342
340
  def bare_repo(
343
- path,
344
- user=None,
345
- group=None,
341
+ path: str,
342
+ user: str | None = None,
343
+ group: str | None = None,
346
344
  present=True,
347
345
  ):
348
346
  """
@@ -363,7 +361,7 @@ def bare_repo(
363
361
  )
364
362
  """
365
363
 
366
- yield from files.directory(path, present=present)
364
+ yield from files.directory._inner(path, present=present)
367
365
 
368
366
  if present:
369
367
  head_filename = unix_path_join(path, "HEAD")
@@ -2,6 +2,8 @@
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
@@ -10,10 +12,10 @@ from pyinfra.facts.iptables import Ip6tablesChains, Ip6tablesRules, IptablesChai
10
12
 
11
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
  """
@@ -58,32 +60,32 @@ def chain(
58
60
 
59
61
  @operation()
60
62
  def rule(
61
- chain,
62
- jump,
63
- present=True,
64
- table="filter",
65
- append=True,
66
- version=4,
63
+ chain: str,
64
+ jump: str,
65
+ present: bool = True,
66
+ table: str = "filter",
67
+ append: bool = True,
68
+ version: int = 4,
67
69
  # Core iptables filter arguments
68
- protocol=None,
69
- not_protocol=None,
70
- source=None,
71
- not_source=None,
72
- destination=None,
73
- not_destination=None,
74
- in_interface=None,
75
- not_in_interface=None,
76
- out_interface=None,
77
- 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,
78
80
  # After-rule arguments
79
- to_destination=None,
80
- to_source=None,
81
- to_ports=None,
82
- 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,
83
85
  # Extras and extra shortcuts
84
- destination_port=None,
85
- source_port=None,
86
- extras="",
86
+ destination_port: int | None = None,
87
+ source_port: int | None = None,
88
+ extras: str = "",
87
89
  ):
88
90
  """
89
91
  Add/remove iptables rules.
@@ -263,6 +265,9 @@ def rule(
263
265
  if source:
264
266
  args.extend(("-s", source))
265
267
 
268
+ if destination:
269
+ args.extend(("-d", destination))
270
+
266
271
  if in_interface:
267
272
  args.extend(("-i", in_interface))
268
273
 
@@ -275,6 +280,9 @@ def rule(
275
280
  if not_source:
276
281
  args.extend(("!", "-s", not_source))
277
282
 
283
+ if not_destination:
284
+ args.extend(("!", "-d", not_destination))
285
+
278
286
  if not_in_interface:
279
287
  args.extend(("!", "-i", not_in_interface))
280
288
 
@@ -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
@@ -11,10 +13,10 @@ from .util.service import handle_service_control
11
13
 
12
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
21
  @operation()
19
22
  def container(
20
- id,
23
+ id: str,
21
24
  present=True,
22
25
  image="ubuntu:16.04",
23
26
  ):
@@ -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.
@@ -54,27 +56,28 @@ def sql(
54
56
 
55
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
 
@@ -269,7 +272,7 @@ def user(
269
272
  # If we're here either the user exists or we just created them; either way
270
273
  # now we can check any privileges are set.
271
274
  if privileges:
272
- yield from _privileges(
275
+ yield from _privileges._inner(
273
276
  user,
274
277
  privileges,
275
278
  user_hostname=user_hostname,
@@ -282,24 +285,25 @@ def user(
282
285
 
283
286
  @operation()
284
287
  def database(
285
- database,
288
+ database: str,
286
289
  # Desired database settings
287
- present=True,
288
- collate=None,
289
- charset=None,
290
- user=None,
291
- user_hostname="localhost",
292
- 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",
293
296
  # Details for speaking to MySQL via `mysql` CLI
294
- mysql_user=None,
295
- mysql_password=None,
296
- mysql_host=None,
297
- 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,
298
301
  ):
302
+ ...
299
303
  """
300
304
  Add/remove MySQL databases.
301
305
 
302
- + name: the name of the database
306
+ + database: the name of the database
303
307
  + present: whether the database should exist or not
304
308
  + collate: the collate to use when creating the database
305
309
  + charset: the charset to use when creating the database
@@ -370,7 +374,7 @@ def database(
370
374
 
371
375
  # Ensure any user privileges for this database
372
376
  if user and user_privileges:
373
- yield from privileges(
377
+ yield from privileges._inner(
374
378
  user,
375
379
  user_hostname=user_hostname,
376
380
  privileges=user_privileges,
@@ -384,18 +388,18 @@ def database(
384
388
 
385
389
  @operation()
386
390
  def privileges(
387
- user,
388
- privileges,
391
+ user: str,
392
+ privileges: str | list[str] | set[str],
389
393
  user_hostname="localhost",
390
394
  database="*",
391
395
  table="*",
392
396
  flush=True,
393
397
  with_grant_option=False,
394
398
  # Details for speaking to MySQL via `mysql` CLI
395
- mysql_user=None,
396
- mysql_password=None,
397
- mysql_host=None,
398
- 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,
399
403
  ):
400
404
  """
401
405
  Add/remove MySQL privileges for a user, either global, database or table specific.
@@ -524,13 +528,13 @@ _privileges = privileges # noqa: E305 (for use where kwarg is the same)
524
528
 
525
529
  @operation(is_idempotent=False)
526
530
  def dump(
527
- dest,
528
- database=None,
531
+ dest: str,
532
+ database: str | None = None,
529
533
  # Details for speaking to MySQL via `mysql` CLI
530
- mysql_user=None,
531
- mysql_password=None,
532
- mysql_host=None,
533
- 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,
534
538
  ):
535
539
  """
536
540
  Dump a MySQL database into a ``.sql`` file. Requires ``mysqldump``.
@@ -565,13 +569,13 @@ def dump(
565
569
 
566
570
  @operation(is_idempotent=False)
567
571
  def load(
568
- src,
569
- database=None,
572
+ src: str,
573
+ database: str | None = None,
570
574
  # Details for speaking to MySQL via `mysql` CLI
571
- mysql_user=None,
572
- mysql_password=None,
573
- mysql_host=None,
574
- 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,
575
579
  ):
576
580
  """
577
581
  Load ``.sql`` file into a database.
pyinfra/operations/npm.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Manage npm (aka node aka Node.js) packages.
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.npm import NpmPackages
@@ -10,7 +12,12 @@ from .util.packaging import ensure_packages
10
12
 
11
13
 
12
14
  @operation()
13
- def packages(packages=None, present=True, latest=False, directory=None):
15
+ def packages(
16
+ packages: str | list[str] | None = None,
17
+ present=True,
18
+ latest=False,
19
+ directory: str | None = None,
20
+ ):
14
21
  """
15
22
  Install/remove/update npm packages.
16
23
 
@@ -2,6 +2,8 @@
2
2
  Manage OpenRC init 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.openrc import OpenrcEnabled, OpenrcStatus
@@ -11,12 +13,12 @@ from .util.service import handle_service_control
11
13
 
12
14
  @operation()
13
15
  def service(
14
- service,
16
+ service: str,
15
17
  running=True,
16
18
  restarted=False,
17
19
  reloaded=False,
18
- command=None,
19
- enabled=None,
20
+ command: str | None = None,
21
+ enabled: bool | None = None,
20
22
  runlevel="default",
21
23
  ):
22
24
  """
@@ -2,6 +2,8 @@
2
2
  Manage pacman packages. (Arch Linux package manager)
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.pacman import PacmanPackages, PacmanUnpackGroup
@@ -18,7 +20,7 @@ def upgrade():
18
20
  yield "pacman --noconfirm -Su"
19
21
 
20
22
 
21
- _upgrade = upgrade # noqa: E305
23
+ _upgrade = upgrade._inner # noqa: E305
22
24
 
23
25
 
24
26
  @operation(is_idempotent=False)
@@ -30,12 +32,12 @@ def update():
30
32
  yield "pacman -Sy"
31
33
 
32
34
 
33
- _update = update # noqa: E305
35
+ _update = update._inner # noqa: E305
34
36
 
35
37
 
36
38
  @operation()
37
39
  def packages(
38
- packages=None,
40
+ packages: str | list[str] | None = None,
39
41
  present=True,
40
42
  update=False,
41
43
  upgrade=False,
@@ -75,8 +77,5 @@ def packages(
75
77
  present,
76
78
  install_command="pacman --noconfirm -S",
77
79
  uninstall_command="pacman --noconfirm -R",
78
- expand_package_fact=lambda package: host.get_fact(
79
- PacmanUnpackGroup,
80
- name=package,
81
- ),
80
+ expand_package_fact=lambda package: host.get_fact(PacmanUnpackGroup, package=package),
82
81
  )
pyinfra/operations/pip.py CHANGED
@@ -3,6 +3,8 @@ Manage pip (python) packages. Compatible globally or inside
3
3
  a virtualenv (virtual environment).
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  from pyinfra import host
7
9
  from pyinfra.api import operation
8
10
  from pyinfra.facts.files import File
@@ -14,8 +16,8 @@ from .util.packaging import ensure_packages
14
16
 
15
17
  @operation()
16
18
  def virtualenv(
17
- path,
18
- python=None,
19
+ path: str,
20
+ python: str | None = None,
19
21
  venv=False,
20
22
  site_packages=False,
21
23
  always_copy=False,
@@ -46,7 +48,7 @@ def virtualenv(
46
48
 
47
49
  if present is False:
48
50
  if host.get_fact(File, path=activate_script_path):
49
- yield from files.directory(path, present=False)
51
+ yield from files.directory._inner(path, present=False)
50
52
  else:
51
53
  host.noop("virtualenv {0} does not exist".format(path))
52
54
 
@@ -76,13 +78,13 @@ def virtualenv(
76
78
  host.noop("virtualenv {0} exists".format(path))
77
79
 
78
80
 
79
- _virtualenv = virtualenv # noqa
81
+ _virtualenv = virtualenv._inner # noqa
80
82
 
81
83
 
82
84
  @operation()
83
85
  def venv(
84
- path,
85
- python=None,
86
+ path: str,
87
+ python: str | None = None,
86
88
  site_packages=False,
87
89
  always_copy=False,
88
90
  present=True,
@@ -105,7 +107,7 @@ def venv(
105
107
  )
106
108
  """
107
109
 
108
- yield from virtualenv(
110
+ yield from _virtualenv(
109
111
  venv=True,
110
112
  path=path,
111
113
  python=python,
@@ -117,14 +119,14 @@ def venv(
117
119
 
118
120
  @operation()
119
121
  def packages(
120
- packages=None,
122
+ packages: str | list[str] | None = None,
121
123
  present=True,
122
124
  latest=False,
123
- requirements=None,
125
+ requirements: str | None = None,
124
126
  pip="pip",
125
- virtualenv=None,
126
- virtualenv_kwargs=None,
127
- extra_install_args=None,
127
+ virtualenv: str | None = None,
128
+ virtualenv_kwargs: dict | None = None,
129
+ extra_install_args: str | None = None,
128
130
  ):
129
131
  """
130
132
  Install/remove/update pip packages.
@@ -185,6 +187,11 @@ def packages(
185
187
  if packages:
186
188
  current_packages = host.get_fact(PipPackages, pip=pip)
187
189
 
190
+ # PEP-0426 states that Python packages should be compared using lowercase, so lowercase both
191
+ # the input packages and the fact packages before comparison.
192
+ packages = [pkg.lower() for pkg in packages]
193
+ current_packages = {pkg.lower(): versions for pkg, versions in current_packages.items()}
194
+
188
195
  yield from ensure_packages(
189
196
  host,
190
197
  packages,
pyinfra/operations/pkg.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Manage BSD packages and repositories. Note that BSD package names are case-sensitive.
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.files import File
@@ -12,7 +14,7 @@ from .util.packaging import ensure_packages
12
14
 
13
15
 
14
16
  @operation()
15
- def packages(packages=None, present=True, pkg_path=None):
17
+ def packages(packages: str | list[str] | None = None, present=True, pkg_path: str | None = None):
16
18
  """
17
19
  Install/remove/update pkg packages. This will use ``pkg ...`` where available
18
20
  (FreeBSD) and the ``pkg_*`` variants elsewhere.