pyinfra 3.0b0__py2.py3-none-any.whl → 3.0b2__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 (115) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +12 -5
  3. pyinfra/api/arguments_typed.py +19 -6
  4. pyinfra/api/command.py +5 -3
  5. pyinfra/api/config.py +115 -13
  6. pyinfra/api/connectors.py +5 -2
  7. pyinfra/api/exceptions.py +19 -0
  8. pyinfra/api/facts.py +34 -33
  9. pyinfra/api/host.py +51 -12
  10. pyinfra/api/inventory.py +4 -0
  11. pyinfra/api/operation.py +88 -42
  12. pyinfra/api/operations.py +10 -11
  13. pyinfra/api/state.py +11 -2
  14. pyinfra/api/util.py +24 -16
  15. pyinfra/connectors/base.py +4 -7
  16. pyinfra/connectors/chroot.py +5 -6
  17. pyinfra/connectors/docker.py +13 -19
  18. pyinfra/connectors/dockerssh.py +5 -4
  19. pyinfra/connectors/local.py +7 -7
  20. pyinfra/connectors/ssh.py +46 -25
  21. pyinfra/connectors/terraform.py +9 -6
  22. pyinfra/connectors/util.py +7 -8
  23. pyinfra/connectors/vagrant.py +11 -10
  24. pyinfra/context.py +1 -0
  25. pyinfra/facts/apk.py +2 -0
  26. pyinfra/facts/apt.py +2 -0
  27. pyinfra/facts/brew.py +2 -0
  28. pyinfra/facts/bsdinit.py +2 -0
  29. pyinfra/facts/cargo.py +2 -0
  30. pyinfra/facts/choco.py +3 -1
  31. pyinfra/facts/deb.py +9 -4
  32. pyinfra/facts/dnf.py +2 -0
  33. pyinfra/facts/docker.py +2 -0
  34. pyinfra/facts/files.py +2 -0
  35. pyinfra/facts/gem.py +2 -0
  36. pyinfra/facts/gpg.py +2 -0
  37. pyinfra/facts/hardware.py +30 -22
  38. pyinfra/facts/launchd.py +2 -0
  39. pyinfra/facts/lxd.py +2 -0
  40. pyinfra/facts/mysql.py +12 -6
  41. pyinfra/facts/npm.py +1 -0
  42. pyinfra/facts/openrc.py +2 -0
  43. pyinfra/facts/pacman.py +6 -2
  44. pyinfra/facts/pip.py +2 -0
  45. pyinfra/facts/pkg.py +2 -0
  46. pyinfra/facts/pkgin.py +2 -0
  47. pyinfra/facts/postgres.py +168 -0
  48. pyinfra/facts/postgresql.py +5 -162
  49. pyinfra/facts/rpm.py +12 -9
  50. pyinfra/facts/server.py +10 -13
  51. pyinfra/facts/snap.py +2 -0
  52. pyinfra/facts/systemd.py +28 -10
  53. pyinfra/facts/upstart.py +2 -0
  54. pyinfra/facts/util/packaging.py +3 -2
  55. pyinfra/facts/vzctl.py +2 -0
  56. pyinfra/facts/xbps.py +2 -0
  57. pyinfra/facts/yum.py +2 -0
  58. pyinfra/facts/zypper.py +2 -0
  59. pyinfra/operations/apk.py +3 -1
  60. pyinfra/operations/apt.py +16 -18
  61. pyinfra/operations/brew.py +10 -8
  62. pyinfra/operations/bsdinit.py +5 -3
  63. pyinfra/operations/cargo.py +3 -1
  64. pyinfra/operations/choco.py +3 -1
  65. pyinfra/operations/dnf.py +15 -19
  66. pyinfra/operations/files.py +86 -69
  67. pyinfra/operations/gem.py +3 -1
  68. pyinfra/operations/git.py +18 -16
  69. pyinfra/operations/iptables.py +33 -25
  70. pyinfra/operations/launchd.py +5 -6
  71. pyinfra/operations/lxd.py +7 -4
  72. pyinfra/operations/mysql.py +57 -53
  73. pyinfra/operations/npm.py +8 -1
  74. pyinfra/operations/openrc.py +5 -3
  75. pyinfra/operations/pacman.py +4 -5
  76. pyinfra/operations/pip.py +16 -9
  77. pyinfra/operations/pkg.py +3 -1
  78. pyinfra/operations/pkgin.py +3 -1
  79. pyinfra/operations/postgres.py +349 -0
  80. pyinfra/operations/postgresql.py +18 -335
  81. pyinfra/operations/puppet.py +3 -1
  82. pyinfra/operations/python.py +7 -3
  83. pyinfra/operations/selinux.py +42 -16
  84. pyinfra/operations/server.py +48 -43
  85. pyinfra/operations/snap.py +3 -1
  86. pyinfra/operations/ssh.py +12 -10
  87. pyinfra/operations/systemd.py +13 -9
  88. pyinfra/operations/sysvinit.py +6 -4
  89. pyinfra/operations/upstart.py +5 -3
  90. pyinfra/operations/util/files.py +24 -16
  91. pyinfra/operations/util/packaging.py +53 -37
  92. pyinfra/operations/util/service.py +18 -13
  93. pyinfra/operations/vzctl.py +12 -10
  94. pyinfra/operations/xbps.py +3 -1
  95. pyinfra/operations/yum.py +14 -18
  96. pyinfra/operations/zypper.py +8 -9
  97. pyinfra/version.py +5 -2
  98. {pyinfra-3.0b0.dist-info → pyinfra-3.0b2.dist-info}/METADATA +31 -29
  99. pyinfra-3.0b2.dist-info/RECORD +163 -0
  100. {pyinfra-3.0b0.dist-info → pyinfra-3.0b2.dist-info}/WHEEL +1 -1
  101. pyinfra_cli/commands.py +3 -2
  102. pyinfra_cli/inventory.py +38 -19
  103. pyinfra_cli/main.py +2 -0
  104. pyinfra_cli/prints.py +27 -105
  105. pyinfra_cli/util.py +3 -1
  106. tests/test_api/test_api_deploys.py +5 -5
  107. tests/test_api/test_api_operations.py +5 -5
  108. tests/test_connectors/test_ssh.py +105 -0
  109. tests/test_connectors/test_terraform.py +11 -8
  110. tests/test_connectors/test_vagrant.py +6 -6
  111. pyinfra-3.0b0.dist-info/RECORD +0 -162
  112. pyinfra_cli/inventory_dsl.py +0 -23
  113. {pyinfra-3.0b0.dist-info → pyinfra-3.0b2.dist-info}/LICENSE.md +0 -0
  114. {pyinfra-3.0b0.dist-info → pyinfra-3.0b2.dist-info}/entry_points.txt +0 -0
  115. {pyinfra-3.0b0.dist-info → pyinfra-3.0b2.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,8 @@ import traceback
11
11
  from datetime import timedelta
12
12
  from fnmatch import fnmatch
13
13
  from io import StringIO
14
- from typing import Union
14
+ from pathlib import Path
15
+ from typing import IO, Any, Union
15
16
 
16
17
  from jinja2 import TemplateRuntimeError, TemplateSyntaxError, UndefinedError
17
18
 
@@ -30,6 +31,7 @@ from pyinfra.api import (
30
31
  from pyinfra.api.command import make_formatted_string_command
31
32
  from pyinfra.api.util import (
32
33
  get_call_location,
34
+ get_file_io,
33
35
  get_file_sha1,
34
36
  get_path_permissions_mode,
35
37
  get_template,
@@ -58,19 +60,19 @@ from .util.files import adjust_regex, ensure_mode_int, get_timestamp, sed_replac
58
60
 
59
61
  @operation()
60
62
  def download(
61
- src,
62
- dest,
63
- user=None,
64
- group=None,
65
- mode=None,
66
- cache_time=None,
63
+ src: str,
64
+ dest: str,
65
+ user: str | None = None,
66
+ group: str | None = None,
67
+ mode: str | None = None,
68
+ cache_time: int | None = None,
67
69
  force=False,
68
- sha256sum=None,
69
- sha1sum=None,
70
- md5sum=None,
71
- headers=None,
70
+ sha256sum: str | None = None,
71
+ sha1sum: str | None = None,
72
+ md5sum: str | None = None,
73
+ headers: dict[str, str] | None = None,
72
74
  insecure=False,
73
- proxy=None,
75
+ proxy: str | None = None,
74
76
  ):
75
77
  """
76
78
  Download files from remote locations using ``curl`` or ``wget``.
@@ -121,8 +123,8 @@ def download(
121
123
  if cache_time:
122
124
  # Time on files is not tz-aware, and will be the same tz as the server's time,
123
125
  # so we can safely remove the tzinfo from the Date fact before comparison.
124
- cache_time = host.get_fact(Date).replace(tzinfo=None) - timedelta(seconds=cache_time)
125
- if info["mtime"] and info["mtime"] < cache_time:
126
+ ctime = host.get_fact(Date).replace(tzinfo=None) - timedelta(seconds=cache_time)
127
+ if info["mtime"] and info["mtime"] < ctime:
126
128
  download = True
127
129
 
128
130
  if sha1sum:
@@ -225,11 +227,11 @@ def download(
225
227
 
226
228
  @operation()
227
229
  def line(
228
- path,
229
- line,
230
+ path: str,
231
+ line: str,
230
232
  present=True,
231
- replace=None,
232
- flags=None,
233
+ replace: str | None = None,
234
+ flags: list[str] | None = None,
233
235
  backup=False,
234
236
  interpolate_variables=False,
235
237
  escape_regex_characters=False,
@@ -267,7 +269,7 @@ def line(
267
269
  it will be append to the end of the file.
268
270
 
269
271
  Ensure new line:
270
- This will ensure that the ``line`` being appended is always on a seperate new
272
+ This will ensure that the ``line`` being appended is always on a separate new
271
273
  line in case the file doesn't end with a newline character.
272
274
 
273
275
 
@@ -425,10 +427,10 @@ def line(
425
427
 
426
428
  @operation()
427
429
  def replace(
428
- path,
429
- text=None,
430
- replace=None,
431
- flags=None,
430
+ path: str,
431
+ text: str | None = None,
432
+ replace: str | None = None,
433
+ flags: list[str] | None = None,
432
434
  backup=False,
433
435
  interpolate_variables=False,
434
436
  match=None, # deprecated
@@ -499,15 +501,15 @@ def replace(
499
501
 
500
502
  @operation()
501
503
  def sync(
502
- src,
503
- dest,
504
- user=None,
505
- group=None,
506
- mode=None,
507
- dir_mode=None,
504
+ src: str,
505
+ dest: str,
506
+ user: str | None = None,
507
+ group: str | None = None,
508
+ mode: str | None = None,
509
+ dir_mode: str | None = None,
508
510
  delete=False,
509
- exclude=None,
510
- exclude_dir=None,
511
+ exclude: str | list[str] | tuple[str] | None = None,
512
+ exclude_dir: str | list[str] | tuple[str] | None = None,
511
513
  add_deploy_dir=True,
512
514
  ):
513
515
  """
@@ -569,7 +571,7 @@ def sync(
569
571
  put_files = []
570
572
  ensure_dirnames = []
571
573
  for dirpath, dirnames, filenames in os.walk(src, topdown=True):
572
- remote_dirpath = os.path.normpath(os.path.relpath(dirpath, src))
574
+ remote_dirpath = Path(os.path.normpath(os.path.relpath(dirpath, src))).as_posix()
573
575
 
574
576
  # Filter excluded dirs
575
577
  for child_dir in dirnames[:]:
@@ -650,7 +652,7 @@ def show_rsync_warning():
650
652
 
651
653
 
652
654
  @operation(is_idempotent=False)
653
- def rsync(src, dest, flags=["-ax", "--delete"]):
655
+ def rsync(src: str, dest: str, flags: list[str] | None = None):
654
656
  """
655
657
  Use ``rsync`` to sync a local directory to the remote system. This operation will actually call
656
658
  the ``rsync`` binary on your system.
@@ -665,6 +667,8 @@ def rsync(src, dest, flags=["-ax", "--delete"]):
665
667
  global arguments.
666
668
  """
667
669
 
670
+ if flags is None:
671
+ flags = ["-ax", "--delete"]
668
672
  show_rsync_warning()
669
673
 
670
674
  try:
@@ -694,8 +698,8 @@ def _create_remote_dir(remote_filename, user, group):
694
698
  is_idempotent=False,
695
699
  )
696
700
  def get(
697
- src,
698
- dest,
701
+ src: str,
702
+ dest: str,
699
703
  add_deploy_dir=True,
700
704
  create_local_dir=False,
701
705
  force=False,
@@ -754,11 +758,11 @@ def get(
754
758
 
755
759
  @operation()
756
760
  def put(
757
- src,
758
- dest,
759
- user=None,
760
- group=None,
761
- mode=None,
761
+ src: str | IO[Any],
762
+ dest: str,
763
+ user: str | None = None,
764
+ group: str | None = None,
765
+ mode: int | str | bool | None = None,
762
766
  add_deploy_dir=True,
763
767
  create_remote_dir=True,
764
768
  force=False,
@@ -819,6 +823,7 @@ def put(
819
823
 
820
824
  # Assume string filename
821
825
  else:
826
+ assert isinstance(src, (str, Path))
822
827
  # Add deploy directory?
823
828
  if add_deploy_dir and state.cwd:
824
829
  src = os.path.join(state.cwd, src)
@@ -833,7 +838,7 @@ def put(
833
838
  raise IOError("No such file: {0}".format(local_file))
834
839
 
835
840
  if mode is True:
836
- if os.path.isfile(local_file):
841
+ if isinstance(local_file, str) and os.path.isfile(local_file):
837
842
  mode = get_path_permissions_mode(local_file)
838
843
  else:
839
844
  logger.warning(
@@ -847,6 +852,7 @@ def put(
847
852
  remote_file = host.get_fact(File, path=dest)
848
853
 
849
854
  if not remote_file and bool(host.get_fact(Directory, path=dest)):
855
+ assert isinstance(src, str)
850
856
  dest = unix_path_join(dest, os.path.basename(src))
851
857
  remote_file = host.get_fact(File, path=dest)
852
858
 
@@ -903,7 +909,15 @@ def put(
903
909
 
904
910
 
905
911
  @operation()
906
- def template(src, dest, user=None, group=None, mode=None, create_remote_dir=True, **data):
912
+ def template(
913
+ src: str | IO[Any],
914
+ dest: str,
915
+ user: str | None = None,
916
+ group: str | None = None,
917
+ mode: str | None = None,
918
+ create_remote_dir=True,
919
+ **data,
920
+ ):
907
921
  '''
908
922
  Generate a template using jinja2 and write it to the remote system.
909
923
 
@@ -999,7 +1013,7 @@ def template(src, dest, user=None, group=None, mode=None, create_remote_dir=True
999
1013
  line_number = trace_frames[-1][1]
1000
1014
 
1001
1015
  # Quickly read the line in question and one above/below for nicer debugging
1002
- with open(src, "r") as f:
1016
+ with get_file_io(src, "r") as f:
1003
1017
  template_lines = f.readlines()
1004
1018
 
1005
1019
  template_lines = [line.strip() for line in template_lines]
@@ -1012,7 +1026,7 @@ def template(src, dest, user=None, group=None, mode=None, create_remote_dir=True
1012
1026
  e,
1013
1027
  "\n".join(relevant_lines),
1014
1028
  ),
1015
- )
1029
+ ) from None
1016
1030
 
1017
1031
  output_file = StringIO(output)
1018
1032
  # Set the template attribute for nicer debugging
@@ -1053,16 +1067,16 @@ def _raise_or_remove_invalid_path(fs_type, path, force, force_backup, force_back
1053
1067
 
1054
1068
  @operation()
1055
1069
  def link(
1056
- path,
1057
- target=None,
1070
+ path: str,
1071
+ target: str | None = None,
1058
1072
  present=True,
1059
- user=None,
1060
- group=None,
1073
+ user: str | None = None,
1074
+ group: str | None = None,
1061
1075
  symbolic=True,
1062
1076
  create_remote_dir=True,
1063
1077
  force=False,
1064
1078
  force_backup=True,
1065
- force_backup_dir=None,
1079
+ force_backup_dir: str | None = None,
1066
1080
  ):
1067
1081
  """
1068
1082
  Add/remove/update links.
@@ -1160,16 +1174,16 @@ def link(
1160
1174
 
1161
1175
  @operation()
1162
1176
  def file(
1163
- path,
1177
+ path: str,
1164
1178
  present=True,
1165
- user=None,
1166
- group=None,
1167
- mode=None,
1179
+ user: str | None = None,
1180
+ group: str | None = None,
1181
+ mode: int | str | None = None,
1168
1182
  touch=False,
1169
1183
  create_remote_dir=True,
1170
1184
  force=False,
1171
1185
  force_backup=True,
1172
- force_backup_dir=None,
1186
+ force_backup_dir: str | None = None,
1173
1187
  ):
1174
1188
  """
1175
1189
  Add/remove/update files.
@@ -1262,15 +1276,15 @@ def file(
1262
1276
 
1263
1277
  @operation()
1264
1278
  def directory(
1265
- path,
1279
+ path: str,
1266
1280
  present=True,
1267
- user=None,
1268
- group=None,
1269
- mode=None,
1281
+ user: str | None = None,
1282
+ group: str | None = None,
1283
+ mode: int | str | None = None,
1270
1284
  recursive=False,
1271
1285
  force=False,
1272
1286
  force_backup=True,
1273
- force_backup_dir=None,
1287
+ force_backup_dir: str | None = None,
1274
1288
  _no_check_owner_mode=False,
1275
1289
  _no_fail_on_link=False,
1276
1290
  ):
@@ -1363,7 +1377,7 @@ def directory(
1363
1377
 
1364
1378
 
1365
1379
  @operation()
1366
- def flags(path, flags=None, present=True):
1380
+ def flags(path: str, flags: list[str] | None = None, present=True):
1367
1381
  """
1368
1382
  Set/clear file flags.
1369
1383
 
@@ -1412,18 +1426,18 @@ def flags(path, flags=None, present=True):
1412
1426
 
1413
1427
  @operation()
1414
1428
  def block(
1415
- path,
1416
- content=None,
1429
+ path: str,
1430
+ content: str | list[str] | None = None,
1417
1431
  present=True,
1418
- line=None,
1432
+ line: str | None = None,
1419
1433
  backup=False,
1420
1434
  escape_regex_characters=False,
1421
1435
  try_prevent_shell_expansion=False,
1422
1436
  before=False,
1423
1437
  after=False,
1424
- marker=None,
1425
- begin=None,
1426
- end=None,
1438
+ marker: str | None = None,
1439
+ begin: str | None = None,
1440
+ end: str | None = None,
1427
1441
  ):
1428
1442
  """
1429
1443
  Ensure content, surrounded by the appropriate markers, is present (or not) in the file.
@@ -1571,6 +1585,7 @@ def block(
1571
1585
  f"\n{the_block}\n{here}",
1572
1586
  )
1573
1587
  elif current == []: # markers not found and have a pattern to match (not start or end)
1588
+ assert isinstance(line, str)
1574
1589
  regex = adjust_regex(line, escape_regex_characters)
1575
1590
  print_before = "{ print }" if before else ""
1576
1591
  print_after = "{ print }" if after else ""
@@ -1599,9 +1614,11 @@ def block(
1599
1614
  out_prep,
1600
1615
  prog,
1601
1616
  q_path,
1602
- '"' + "\n".join(content) + '"'
1603
- if not try_prevent_shell_expansion
1604
- else "'" + "\n".join(content) + "'",
1617
+ (
1618
+ '"' + "\n".join(content) + '"'
1619
+ if not try_prevent_shell_expansion
1620
+ else "'" + "\n".join(content) + "'"
1621
+ ),
1605
1622
  "> $OUT &&",
1606
1623
  real_out,
1607
1624
  )
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
@@ -14,7 +16,7 @@ from .util.files import chown, unix_path_join
14
16
 
15
17
 
16
18
  @operation()
17
- def config(key, value, multi_value=False, repo=None):
19
+ def config(key: str, value: str, multi_value=False, repo: str | None = None):
18
20
  """
19
21
  Manage git config for a repository or globally.
20
22
 
@@ -62,13 +64,13 @@ def config(key, value, multi_value=False, repo=None):
62
64
 
63
65
  @operation()
64
66
  def repo(
65
- src,
66
- dest,
67
- branch=None,
67
+ src: str,
68
+ dest: str,
69
+ branch: str | None = None,
68
70
  pull=True,
69
71
  rebase=False,
70
- user=None,
71
- group=None,
72
+ user: str | None = None,
73
+ group: str | None = None,
72
74
  ssh_keyscan=False,
73
75
  update_submodules=False,
74
76
  recursive_submodules=False,
@@ -155,19 +157,19 @@ def repo(
155
157
 
156
158
  @operation()
157
159
  def worktree(
158
- worktree,
159
- repo=None,
160
+ worktree: str,
161
+ repo: str | None = None,
160
162
  detached=False,
161
- new_branch=None,
162
- commitish=None,
163
+ new_branch: str | None = None,
164
+ commitish: str | None = None,
163
165
  pull=True,
164
166
  rebase=False,
165
- from_remote_branch=None,
167
+ from_remote_branch: tuple[str, str] | None = None,
166
168
  present=True,
167
169
  assume_repo_exists=False,
168
170
  force=False,
169
- user=None,
170
- group=None,
171
+ user: str | None = None,
172
+ group: str | None = None,
171
173
  ):
172
174
  """
173
175
  Manage git worktrees.
@@ -336,9 +338,9 @@ def worktree(
336
338
 
337
339
  @operation()
338
340
  def bare_repo(
339
- path,
340
- user=None,
341
- group=None,
341
+ path: str,
342
+ user: str | None = None,
343
+ group: str | None = None,
342
344
  present=True,
343
345
  ):
344
346
  """
@@ -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
  ):