pyinfra 3.1__py2.py3-none-any.whl → 3.2__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 (52) hide show
  1. pyinfra/api/arguments.py +10 -3
  2. pyinfra/api/deploy.py +12 -2
  3. pyinfra/api/host.py +7 -4
  4. pyinfra/connectors/chroot.py +1 -1
  5. pyinfra/connectors/docker.py +17 -6
  6. pyinfra/connectors/local.py +1 -1
  7. pyinfra/connectors/ssh.py +3 -0
  8. pyinfra/connectors/sshuserclient/client.py +26 -14
  9. pyinfra/facts/apk.py +3 -1
  10. pyinfra/facts/apt.py +62 -2
  11. pyinfra/facts/crontab.py +190 -0
  12. pyinfra/facts/docker.py +6 -0
  13. pyinfra/facts/efibootmgr.py +108 -0
  14. pyinfra/facts/files.py +93 -6
  15. pyinfra/facts/git.py +3 -2
  16. pyinfra/facts/hardware.py +1 -0
  17. pyinfra/facts/mysql.py +1 -2
  18. pyinfra/facts/opkg.py +233 -0
  19. pyinfra/facts/pipx.py +74 -0
  20. pyinfra/facts/podman.py +47 -0
  21. pyinfra/facts/postgres.py +2 -0
  22. pyinfra/facts/selinux.py +3 -1
  23. pyinfra/facts/server.py +39 -77
  24. pyinfra/facts/util/units.py +30 -0
  25. pyinfra/facts/zfs.py +22 -19
  26. pyinfra/local.py +3 -2
  27. pyinfra/operations/apt.py +29 -21
  28. pyinfra/operations/crontab.py +189 -0
  29. pyinfra/operations/docker.py +13 -12
  30. pyinfra/operations/files.py +20 -2
  31. pyinfra/operations/git.py +48 -9
  32. pyinfra/operations/opkg.py +88 -0
  33. pyinfra/operations/pip.py +3 -2
  34. pyinfra/operations/pipx.py +90 -0
  35. pyinfra/operations/postgres.py +15 -11
  36. pyinfra/operations/runit.py +2 -0
  37. pyinfra/operations/server.py +4 -178
  38. pyinfra/operations/zfs.py +14 -14
  39. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/METADATA +11 -12
  40. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/RECORD +52 -43
  41. pyinfra_cli/inventory.py +26 -9
  42. pyinfra_cli/prints.py +18 -3
  43. pyinfra_cli/util.py +5 -2
  44. tests/test_cli/test_cli_deploy.py +15 -13
  45. tests/test_cli/test_cli_exceptions.py +2 -2
  46. tests/test_cli/test_cli_inventory.py +53 -0
  47. tests/test_cli/test_cli_util.py +2 -4
  48. tests/test_connectors/test_sshuserclient.py +68 -1
  49. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/LICENSE.md +0 -0
  50. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/WHEEL +0 -0
  51. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/entry_points.txt +0 -0
  52. {pyinfra-3.1.dist-info → pyinfra-3.2.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,6 @@ Linux/BSD.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
- import shlex
9
8
  from io import StringIO
10
9
  from itertools import filterfalse, tee
11
10
  from os import path
@@ -18,7 +17,6 @@ from pyinfra.api.util import try_int
18
17
  from pyinfra.connectors.util import remove_any_sudo_askpass_file
19
18
  from pyinfra.facts.files import Directory, FindInFile, Link
20
19
  from pyinfra.facts.server import (
21
- Crontab,
22
20
  Groups,
23
21
  Home,
24
22
  Hostname,
@@ -30,6 +28,7 @@ from pyinfra.facts.server import (
30
28
  Users,
31
29
  Which,
32
30
  )
31
+ from pyinfra.operations import crontab as crontab_
33
32
 
34
33
  from . import (
35
34
  apk,
@@ -49,7 +48,7 @@ from . import (
49
48
  yum,
50
49
  zypper,
51
50
  )
52
- from .util.files import chmod, sed_replace
51
+ from .util.files import chmod
53
52
 
54
53
  if TYPE_CHECKING:
55
54
  from pyinfra.api.arguments_typed import PyinfraOperation
@@ -89,7 +88,7 @@ def reboot(delay=10, interval=1, reboot_timeout=300):
89
88
  sleep(delay)
90
89
  max_retries = round(reboot_timeout / interval)
91
90
 
92
- host.connection = None # remove the connection object
91
+ host.disconnect() # make sure we are properly disconnected
93
92
  retries = 0
94
93
 
95
94
  while True:
@@ -588,180 +587,7 @@ def packages(
588
587
  yield from package_operation._inner(packages=packages, present=present)
589
588
 
590
589
 
591
- @operation()
592
- def crontab(
593
- command: str,
594
- present=True,
595
- user: str | None = None,
596
- cron_name: str | None = None,
597
- minute="*",
598
- hour="*",
599
- month="*",
600
- day_of_week="*",
601
- day_of_month="*",
602
- special_time: str | None = None,
603
- interpolate_variables=False,
604
- ):
605
- """
606
- Add/remove/update crontab entries.
607
-
608
- + command: the command for the cron
609
- + present: whether this cron command should exist
610
- + user: the user whose crontab to manage
611
- + cron_name: name the cronjob so future changes to the command will overwrite
612
- + minute: which minutes to execute the cron
613
- + hour: which hours to execute the cron
614
- + month: which months to execute the cron
615
- + day_of_week: which day of the week to execute the cron
616
- + day_of_month: which day of the month to execute the cron
617
- + special_time: cron "nickname" time (@reboot, @daily, etc), overrides others
618
- + interpolate_variables: whether to interpolate variables in ``command``
619
-
620
- Cron commands:
621
- Unless ``name`` is specified the command is used to identify crontab entries.
622
- This means commands must be unique within a given users crontab. If you require
623
- multiple identical commands, provide a different name argument for each.
624
-
625
- Special times:
626
- When provided, ``special_time`` will be used instead of any values passed in
627
- for ``minute``/``hour``/``month``/``day_of_week``/``day_of_month``.
628
-
629
- **Example:**
630
-
631
- .. code:: python
632
-
633
- # simple example for a crontab
634
- server.crontab(
635
- name="Backup /etc weekly",
636
- command="/bin/tar cf /tmp/etc_bup.tar /etc",
637
- name="backup_etc",
638
- day_of_week=0,
639
- hour=1,
640
- minute=0,
641
- )
642
- """
643
-
644
- def comma_sep(value):
645
- if isinstance(value, (list, tuple)):
646
- return ",".join("{0}".format(v) for v in value)
647
- return value
648
-
649
- minute = comma_sep(minute)
650
- hour = comma_sep(hour)
651
- month = comma_sep(month)
652
- day_of_week = comma_sep(day_of_week)
653
- day_of_month = comma_sep(day_of_month)
654
-
655
- crontab = host.get_fact(Crontab, user=user)
656
- name_comment = "# pyinfra-name={0}".format(cron_name)
657
-
658
- existing_crontab = crontab.get(command)
659
- existing_crontab_command = command
660
- existing_crontab_match = command
661
-
662
- if not existing_crontab and cron_name: # find the crontab by name if provided
663
- for cmd, details in crontab.items():
664
- if not details["comments"]:
665
- continue
666
- if name_comment in details["comments"]:
667
- existing_crontab = details
668
- existing_crontab_match = cmd
669
- existing_crontab_command = cmd
670
-
671
- exists = existing_crontab is not None
672
-
673
- edit_commands: list[str | StringCommand] = []
674
- temp_filename = host.get_temp_filename()
675
-
676
- if special_time:
677
- new_crontab_line = "{0} {1}".format(special_time, command)
678
- else:
679
- new_crontab_line = "{minute} {hour} {day_of_month} {month} {day_of_week} {command}".format(
680
- minute=minute,
681
- hour=hour,
682
- day_of_month=day_of_month,
683
- month=month,
684
- day_of_week=day_of_week,
685
- command=command,
686
- )
687
-
688
- existing_crontab_match = ".*{0}.*".format(existing_crontab_match)
689
-
690
- # Don't want the cron and it does exist? Remove the line
691
- if not present and exists:
692
- edit_commands.append(
693
- sed_replace(
694
- temp_filename,
695
- existing_crontab_match,
696
- "",
697
- interpolate_variables=interpolate_variables,
698
- ),
699
- )
700
-
701
- # Want the cron but it doesn't exist? Append the line
702
- elif present and not exists:
703
- if cron_name:
704
- if crontab: # append a blank line if cron entries already exist
705
- edit_commands.append("echo '' >> {0}".format(temp_filename))
706
- edit_commands.append(
707
- "echo {0} >> {1}".format(
708
- shlex.quote(name_comment),
709
- temp_filename,
710
- ),
711
- )
712
-
713
- edit_commands.append(
714
- "echo {0} >> {1}".format(
715
- shlex.quote(new_crontab_line),
716
- temp_filename,
717
- ),
718
- )
719
-
720
- # We have the cron and it exists, do it's details? If not, replace the line
721
- elif present and exists:
722
- assert existing_crontab is not None
723
- if any(
724
- (
725
- special_time != existing_crontab.get("special_time"),
726
- try_int(minute) != existing_crontab.get("minute"),
727
- try_int(hour) != existing_crontab.get("hour"),
728
- try_int(month) != existing_crontab.get("month"),
729
- try_int(day_of_week) != existing_crontab.get("day_of_week"),
730
- try_int(day_of_month) != existing_crontab.get("day_of_month"),
731
- existing_crontab_command != command,
732
- ),
733
- ):
734
- edit_commands.append(
735
- sed_replace(
736
- temp_filename,
737
- existing_crontab_match,
738
- new_crontab_line,
739
- interpolate_variables=interpolate_variables,
740
- ),
741
- )
742
-
743
- if edit_commands:
744
- crontab_args = []
745
- if user:
746
- crontab_args.append("-u {0}".format(user))
747
-
748
- # List the crontab into a temporary file if it exists
749
- if crontab:
750
- yield "crontab -l {0} > {1}".format(" ".join(crontab_args), temp_filename)
751
-
752
- # Now yield any edits
753
- for edit_command in edit_commands:
754
- yield edit_command
755
-
756
- # Finally, use the tempfile to write a new crontab
757
- yield "crontab {0} {1}".format(" ".join(crontab_args), temp_filename)
758
- else:
759
- host.noop(
760
- "crontab {0} {1}".format(
761
- command,
762
- "exists" if present else "does not exist",
763
- ),
764
- )
590
+ crontab = crontab_.crontab
765
591
 
766
592
 
767
593
  @operation()
pyinfra/operations/zfs.py CHANGED
@@ -4,7 +4,7 @@ Manage ZFS filesystems.
4
4
 
5
5
  from pyinfra import host
6
6
  from pyinfra.api import operation
7
- from pyinfra.facts.zfs import Datasets, Snapshots
7
+ from pyinfra.facts.zfs import ZfsDatasets, ZfsSnapshots
8
8
 
9
9
 
10
10
  @operation()
@@ -33,14 +33,14 @@ def dataset(
33
33
 
34
34
  .. code:: python
35
35
 
36
- zfs.dataset(
37
- "tank/srv",
38
- mountpoint="/srv",
39
- compression="lz4",
40
- properties={"com.sun:auto_snapshot": "true"}
41
- )
42
- zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume
43
- zfs.dataset("tank/home@old_version", present=False)
36
+ zfs.dataset(
37
+ "tank/srv",
38
+ mountpoint="/srv",
39
+ compression="lz4",
40
+ properties={"com.sun:auto_snapshot": "true"}
41
+ )
42
+ zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume
43
+ zfs.dataset("tank/home@old_version", present=False)
44
44
 
45
45
  """
46
46
 
@@ -48,7 +48,7 @@ def dataset(
48
48
 
49
49
  properties.update(extra_props)
50
50
 
51
- datasets = host.get_fact(Datasets)
51
+ datasets = host.get_fact(ZfsDatasets)
52
52
 
53
53
  existing_dataset = datasets.get(dataset_name)
54
54
 
@@ -99,11 +99,11 @@ def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extr
99
99
 
100
100
  .. code:: python
101
101
 
102
- zfs.snapshot("tank/home@weekly_backup")
102
+ zfs.snapshot("tank/home@weekly_backup")
103
103
 
104
104
  """
105
105
  properties.update(extra_props)
106
- snapshots = host.get_fact(Snapshots)
106
+ snapshots = host.get_fact(ZfsSnapshots)
107
107
 
108
108
  if snapshot_name in snapshots or not present:
109
109
  yield from dataset._inner(snapshot_name, present=present, properties=properties)
@@ -134,7 +134,7 @@ def volume(
134
134
 
135
135
  .. code:: python
136
136
 
137
- zfs.volume("tank/vm-disks/db_srv_04", "32G")
137
+ zfs.volume("tank/vm-disks/db_srv_04", "32G")
138
138
 
139
139
  """
140
140
  properties.update(extra_props)
@@ -163,7 +163,7 @@ def filesystem(fs_name, present=True, recursive=False, properties={}, **extra_pr
163
163
 
164
164
  .. code:: python
165
165
 
166
- zfs.filesystem("tank/vm-disks/db_srv_04", "32G")
166
+ zfs.filesystem("tank/vm-disks/db_srv_04", "32G")
167
167
 
168
168
  """
169
169
  properties.update(extra_props)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -33,7 +33,6 @@ Requires-Dist: click >2
33
33
  Requires-Dist: jinja2 <4,>2
34
34
  Requires-Dist: python-dateutil <3,>2
35
35
  Requires-Dist: setuptools
36
- Requires-Dist: configparser
37
36
  Requires-Dist: pywinrm
38
37
  Requires-Dist: typeguard
39
38
  Requires-Dist: distro <2,>=1.6
@@ -42,12 +41,12 @@ Requires-Dist: importlib-metadata >=3.6 ; python_version < "3.10"
42
41
  Requires-Dist: typing-extensions ; python_version < "3.11"
43
42
  Requires-Dist: graphlib-backport ; python_version < "3.9"
44
43
  Provides-Extra: dev
45
- Requires-Dist: pytest ==8.2.1 ; extra == 'dev'
46
- Requires-Dist: coverage ==7.5.1 ; extra == 'dev'
44
+ Requires-Dist: pytest ==8.3.3 ; extra == 'dev'
45
+ Requires-Dist: coverage ==7.6.1 ; extra == 'dev'
47
46
  Requires-Dist: pytest-cov ==5.0.0 ; extra == 'dev'
48
- Requires-Dist: black ==24.4.2 ; extra == 'dev'
47
+ Requires-Dist: black ==24.8.0 ; extra == 'dev'
49
48
  Requires-Dist: isort ==5.13.2 ; extra == 'dev'
50
- Requires-Dist: flake8 ==7.0.0 ; extra == 'dev'
49
+ Requires-Dist: flake8 ==7.1.1 ; extra == 'dev'
51
50
  Requires-Dist: flake8-black ==0.3.6 ; extra == 'dev'
52
51
  Requires-Dist: flake8-isort ==6.1.1 ; extra == 'dev'
53
52
  Requires-Dist: mypy ; extra == 'dev'
@@ -57,7 +56,7 @@ Requires-Dist: types-python-dateutil ; extra == 'dev'
57
56
  Requires-Dist: types-PyYAML ; extra == 'dev'
58
57
  Requires-Dist: types-setuptools ; extra == 'dev'
59
58
  Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'dev'
60
- Requires-Dist: myst-parser ==2.0.0 ; extra == 'dev'
59
+ Requires-Dist: myst-parser ==3.0.1 ; extra == 'dev'
61
60
  Requires-Dist: sphinx ==6.2.1 ; extra == 'dev'
62
61
  Requires-Dist: wheel ; extra == 'dev'
63
62
  Requires-Dist: twine ; extra == 'dev'
@@ -68,15 +67,15 @@ Requires-Dist: flake8-spellcheck ==0.12.1 ; extra == 'dev'
68
67
  Requires-Dist: redbaron ; extra == 'dev'
69
68
  Provides-Extra: docs
70
69
  Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'docs'
71
- Requires-Dist: myst-parser ==2.0.0 ; extra == 'docs'
70
+ Requires-Dist: myst-parser ==3.0.1 ; extra == 'docs'
72
71
  Requires-Dist: sphinx ==6.2.1 ; extra == 'docs'
73
72
  Provides-Extra: test
74
- Requires-Dist: pytest ==8.2.1 ; extra == 'test'
75
- Requires-Dist: coverage ==7.5.1 ; extra == 'test'
73
+ Requires-Dist: pytest ==8.3.3 ; extra == 'test'
74
+ Requires-Dist: coverage ==7.6.1 ; extra == 'test'
76
75
  Requires-Dist: pytest-cov ==5.0.0 ; extra == 'test'
77
- Requires-Dist: black ==24.4.2 ; extra == 'test'
76
+ Requires-Dist: black ==24.8.0 ; extra == 'test'
78
77
  Requires-Dist: isort ==5.13.2 ; extra == 'test'
79
- Requires-Dist: flake8 ==7.0.0 ; extra == 'test'
78
+ Requires-Dist: flake8 ==7.1.1 ; extra == 'test'
80
79
  Requires-Dist: flake8-black ==0.3.6 ; extra == 'test'
81
80
  Requires-Dist: flake8-isort ==6.1.1 ; extra == 'test'
82
81
  Requires-Dist: mypy ; extra == 'test'
@@ -1,21 +1,21 @@
1
1
  pyinfra/__init__.py,sha256=7ZcKHGWk7_nYxsYrbFBB_vJr-J-Ddbc56ZS4sk5ArVw,535
2
2
  pyinfra/__main__.py,sha256=aVd00glLz5CMJGXgt1XxbOvC2HluqaowoTOjxgIpBaA,47
3
3
  pyinfra/context.py,sha256=S6DvGjjTEjM4u2m9oIAmAaV7kXIJzVwYf725P1muIuY,3395
4
- pyinfra/local.py,sha256=0bpIRCyDKM6i_jA1i8Ej2qr_iWIF9cUYWutXNdLj8po,2751
4
+ pyinfra/local.py,sha256=wT84xkJc9UBN5isvIVbNpm2fzZaadwE-dkbAwaFQdZk,2808
5
5
  pyinfra/progress.py,sha256=X3hXZ4Flh_L9FE4ZEWxWoG0R4dA5UPd1FCO-Exd5Xtc,4193
6
6
  pyinfra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  pyinfra/version.py,sha256=LZf50PHDzEZv65w0G-iMICoQ9US0U5LWHAOEmNtkF3I,216
8
8
  pyinfra/api/__init__.py,sha256=suGbKKM-qCduuXFYBEcyswlTqozewtYpdLRhK63PVn0,942
9
- pyinfra/api/arguments.py,sha256=8BhuJiljFJzaCi9lM8HoEBB9mcnCBilv4Zwdx_349g8,9941
9
+ pyinfra/api/arguments.py,sha256=GdkwOpBOUKCDer6gxXtChKmRD8iDr_1pQMXXv7jzfDw,10266
10
10
  pyinfra/api/arguments_typed.py,sha256=WQKr0wDtlgJGq-Vkv_oPAz7f-LxjqQv3wJCdvVrePWk,2331
11
11
  pyinfra/api/command.py,sha256=SyUlxvhYlXpgFpg0jua8bzQ2KPtVYQXHcvD6AUL2SCI,7226
12
12
  pyinfra/api/config.py,sha256=wS6Pi4T4DxNkzO4llNY-ghLxyI5VBJ26uGvgMPZxIKY,9043
13
13
  pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
14
14
  pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
15
- pyinfra/api/deploy.py,sha256=xo4F7URUf3xzIChRHZn4zwqs_WTjLjZNC9i9eQjAFk8,2756
15
+ pyinfra/api/deploy.py,sha256=CllKp1QYLseeMStj35waZazB0hUuy3iGvDt7b_lH31M,3138
16
16
  pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
17
17
  pyinfra/api/facts.py,sha256=aMPtkB7vypyXRQDThjwJZzAnEgqjP0wrwyEhRHQf4Js,9449
18
- pyinfra/api/host.py,sha256=KHDzgCez_rMoW4YkX_2lnbL56tJDwvHHA1eeDCvBtVA,13728
18
+ pyinfra/api/host.py,sha256=bsYWn_kNQdWsk6y5ExCJ4FYVq3KlgfHSPnciC0MsrqE,13853
19
19
  pyinfra/api/inventory.py,sha256=nPITdNEJ7q71adIqS_OKHsMjD7amUuHEuTl6xzgh1Gk,7734
20
20
  pyinfra/api/operation.py,sha256=Dp7pH9H3EYs7U1ZvquYUbOtWJPO9iIAa4H7GwXdxFxs,15170
21
21
  pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
@@ -23,50 +23,55 @@ pyinfra/api/state.py,sha256=3dXRjeZJXnzLcbP9E4aogkRPwIg3_kK1h4Tf4FVZock,12622
23
23
  pyinfra/api/util.py,sha256=K4aFjGW7KAz2ZQqfRriRqyHMCQFFrX06WPola3epjaE,12410
24
24
  pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  pyinfra/connectors/base.py,sha256=2fASiV-MvpXgcZAFLM_PUwYx5ax6EHai44ri_oEKeSE,3732
26
- pyinfra/connectors/chroot.py,sha256=Xd72I8T58KIwKOoc0LXCw91AoEIaiHfRLDcDVTHGJ0o,5931
27
- pyinfra/connectors/docker.py,sha256=2UNHhXS4hpLo7I19ixDeSd7JR8SNo43VgqsaUIZQZJ4,8741
26
+ pyinfra/connectors/chroot.py,sha256=X5U3vi-zPm4DV-lHTwoHU8iEl8dppDiYJoWP_1mJVFY,5919
27
+ pyinfra/connectors/docker.py,sha256=GwWTx8Mx1AnKl0vUNSvbyoHTjQNPS0njR-tA4lNViAU,9196
28
28
  pyinfra/connectors/dockerssh.py,sha256=VWHY--jqs3yf-RuPUZXav4vLeON9SzoVC9CUyOJo1rg,8919
29
- pyinfra/connectors/local.py,sha256=vYOBQS_5rf-dVaPeG4dJlLwBHqkxAzLjj3aDEgbAsx8,6900
30
- pyinfra/connectors/ssh.py,sha256=F6LeotcbnxPPECGBTAIGbGwMJzEc3MAHGAsigoDkfYQ,21130
29
+ pyinfra/connectors/local.py,sha256=eFDrBalS1yPCIUdQzh8h2HF3VP2nDn0uVFKOeeQZfiw,6888
30
+ pyinfra/connectors/ssh.py,sha256=Pr7W_aiGstkWQqrwm-cNENzhzTWTBLWM-XAAbF9uOEQ,21212
31
31
  pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
32
  pyinfra/connectors/terraform.py,sha256=Tu59cbemll5CfqlIaQtOrLa0HKzl23c64ih0DZXJu1I,4227
33
33
  pyinfra/connectors/util.py,sha256=0bvoMsGMD-Tbfaer8NUhWJjBnaNKdmE83PDg48BYjcU,11374
34
34
  pyinfra/connectors/vagrant.py,sha256=oEeRglzRmemRXW3vilsp_Xg9qnZMRprRJO9fd_C-f5M,4759
35
35
  pyinfra/connectors/sshuserclient/__init__.py,sha256=Qc4RO2wknSWIiNTwOeQ0y2TeiuKHmyWDW2Dz4MOo9CE,44
36
- pyinfra/connectors/sshuserclient/client.py,sha256=24KWAAqIaUPQIod-CSeXKkA_WhQnIIGWaLKlnVUATDY,9746
36
+ pyinfra/connectors/sshuserclient/client.py,sha256=6tN-dJYxho0nmyfXRLeaEs6EtdPdDNNACROOuZSoUos,10037
37
37
  pyinfra/connectors/sshuserclient/config.py,sha256=UMwkvTgAIS7__re6Wz_pwH6EU4kO1-uMQ5zuFakH0v4,2721
38
38
  pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
39
- pyinfra/facts/apk.py,sha256=q76WdaCNZGKzYia5vMscCsOi4WlnBhcj_9c7Jj-2LqQ,581
40
- pyinfra/facts/apt.py,sha256=t1Us_ABkFRhzZwtAItUNXgvmPUqmzSRHhc20qODBx1g,2172
39
+ pyinfra/facts/apk.py,sha256=83kr2SpqFGXuH5Z0T5dF2zbn9rM3Eh7RzAQvOwMWuC8,717
40
+ pyinfra/facts/apt.py,sha256=Ou5FazOmJFzNtfveH-4vuZH8LdHlUBR7UGRgUX_7oEY,3904
41
41
  pyinfra/facts/brew.py,sha256=qDz89ZlZOiCZv0GLOXOjgFtqq66SLaYXgCncYP2LwDs,2584
42
42
  pyinfra/facts/bsdinit.py,sha256=hyESeGu0hPf8HY1D0bIFFFNFpXRdZB2R52aflVQPf9o,577
43
43
  pyinfra/facts/cargo.py,sha256=OQF6nOulp2TIaFK1fiAEevsXnL5OMQUL6LkFHidb1yo,605
44
44
  pyinfra/facts/choco.py,sha256=A0VCXnI5H9RocgO1IvaNWRIxnXiIZYEzIDG1F-ydJi4,790
45
+ pyinfra/facts/crontab.py,sha256=dTiAZPXV6DCi2BeEl33PP5k8c9ODkzgIDD6cPC5l2pA,5674
45
46
  pyinfra/facts/deb.py,sha256=XGyxnow9wjpE8ZKTZDa1_SNChMyMcNgFeTG1ka5uky4,1922
46
47
  pyinfra/facts/dnf.py,sha256=9rTBgLHewbk8XCJuikzAYCumfFAzbmmHMchlaXBhdWw,977
47
- pyinfra/facts/docker.py,sha256=CVSsUEiBaQrNvM1mggoKCXj5DdzwmcbufUY96koqKBw,2250
48
- pyinfra/facts/files.py,sha256=qJw5_xnPxS5orFy2Ch4oUjmiFUFGGe71UEXoXdZBSek,11765
48
+ pyinfra/facts/docker.py,sha256=tA-H2MP54iBkV5zgpCaTmPEZH6P-n59E5lRXplJ1iBk,2474
49
+ pyinfra/facts/efibootmgr.py,sha256=L8hIYLxRTNAgIdf7djZ3z-TylbnMnbagl7OuJQXHKWE,3473
50
+ pyinfra/facts/files.py,sha256=E4eS15wpNgCmlpNFWRYJuuy6DcPCzhzULwp1sJ-iyqg,15077
49
51
  pyinfra/facts/flatpak.py,sha256=lnuFZYGPtPDe35YXTVgn5M0rhgcA5ys3fMI6EmvCnm4,1536
50
52
  pyinfra/facts/gem.py,sha256=ktC2hofSwYX0fVcdWleU74ddjW1RPZvKMW-3wYp9lJE,572
51
- pyinfra/facts/git.py,sha256=6e_2GjDT2oAxdtkHTyeMYQ9N64gZDorLTTVeZhFel18,1276
53
+ pyinfra/facts/git.py,sha256=-UhY10Jx8MO537Dstm_jsn42Ezx5SsUZT315Qx7b4C0,1347
52
54
  pyinfra/facts/gpg.py,sha256=wYKoQl4aHXB1UqqbWCdVhUoa6N6Liz01AmH8fPjxR48,3813
53
- pyinfra/facts/hardware.py,sha256=YUK1rZ2tygry9_m7HjV3TQ_GZOW7WHqTlonsOT-zRxk,12022
55
+ pyinfra/facts/hardware.py,sha256=9UOgvzlJ7jx7hPfmkhldYEymKEiKMR3ZVFGfZXl_oe0,12023
54
56
  pyinfra/facts/iptables.py,sha256=sUkywfHZUXnMZF_KshAnyJufrJvZ9fBYnERSRbwOCRE,3374
55
57
  pyinfra/facts/launchd.py,sha256=D0RSpBQXt4a4zJOxwxIb5RYY34sAx6ZrWj8NxwYqhIc,767
56
58
  pyinfra/facts/lxd.py,sha256=NeIMK9VsDXlRf7q3j17oYbdigL7XjLnhmqv63t6CrzQ,466
57
- pyinfra/facts/mysql.py,sha256=dHEn3E5rTe4aw3DNHikmgj_VBkQeJZs-gfuS5N35BqE,6135
59
+ pyinfra/facts/mysql.py,sha256=w2XphDMeEFBrvWp5MK_DC0_7GoI1RYlheovjhuIiTBw,6123
58
60
  pyinfra/facts/npm.py,sha256=rI3at2M6Rfwb4cPj5gTivMTss5POs5_W2Ilcrgv8XAA,756
59
61
  pyinfra/facts/openrc.py,sha256=gAXl3evsDXSHcIKIj4E_CRerAugEFpGGNRxJBr0rBAk,1490
62
+ pyinfra/facts/opkg.py,sha256=bnVOPnLIevAKMb_dnpSohzRmTTbVmSo7R5H1XrChfgI,7210
60
63
  pyinfra/facts/pacman.py,sha256=NJ43rYc5k4uhl5sFxd9sTg7yqTQhB3xsREsusS6BzwU,1198
61
64
  pyinfra/facts/pip.py,sha256=ORyrVxu_-8eIr0uSCkI6x4MH-sorDEavml4aozcdrOA,738
65
+ pyinfra/facts/pipx.py,sha256=5iZSnhYq8ZZ8dOhEmek254_nXT1nEm4BulU56TPnGuM,1730
62
66
  pyinfra/facts/pkg.py,sha256=iIvinsJxEaSYAaRw_5_WD5WlIlcy4ksKa0lwj0CFxco,520
63
67
  pyinfra/facts/pkgin.py,sha256=Y4QfUwtWcBH-h77O-MtiixSepHK0p-lcebrqdmABfs8,580
64
- pyinfra/facts/postgres.py,sha256=7cg0Nq8wNIWnKw3B8dJpgjSFZ7q90-_NhwEw2NsSJm8,4229
68
+ pyinfra/facts/podman.py,sha256=4vk2qhfFFd66tAE9zVjNUkvDTbntjrTkMkhMEvjQIXo,1091
69
+ pyinfra/facts/postgres.py,sha256=K_2JCJhmulJeyVPE5jAJhpgPpM7OF5LVJcfgvRg6icg,4293
65
70
  pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
66
71
  pyinfra/facts/rpm.py,sha256=SzHNCNUMA-j5uJl4PKRTFpckOvNZ2zpxNeQyOCl8Usk,2225
67
72
  pyinfra/facts/runit.py,sha256=iarF_Tql8bkNeHsKGRANRKNyBWwMsflsTNXj1Wz14i8,2021
68
- pyinfra/facts/selinux.py,sha256=zzh7o0SU8ocMRJzcYMuAv6ss93onfojpa1pAN1azgeU,4433
69
- pyinfra/facts/server.py,sha256=fDXSNNlZghJTGqr9CWRDob-_N-8xxb-KUZlTf5No-M0,20439
73
+ pyinfra/facts/selinux.py,sha256=as3AvC6p88er0rYBFTdIWf6k3w0pVjDqDAV9Ur5zY90,4443
74
+ pyinfra/facts/server.py,sha256=bJ9gg8q_wUKwdJgETbXhOVRiJ-4WKkZn30qeeFhQT9A,19064
70
75
  pyinfra/facts/snap.py,sha256=6br9IMIoq88z_RS0FLXxfodIVjUmyPU9eZBa9zO8H1o,2027
71
76
  pyinfra/facts/systemd.py,sha256=RS6pdcgpIvWMbQeT93O57EKXQzFzR0tF29lCAJQmaAk,4227
72
77
  pyinfra/facts/sysvinit.py,sha256=RniaROHyeZD3jVOa_sISpZV4zx8ae8HkUQrtriLIlWc,1521
@@ -74,42 +79,46 @@ pyinfra/facts/upstart.py,sha256=HYR7vJ6oqtuRhxXQgzGDKYzyKqqVsjT-TtPPWOjBGdA,635
74
79
  pyinfra/facts/vzctl.py,sha256=S9aclpDBF3DmBLwMltsd9j3B4QxQ5-1Kb1hybZodEqI,678
75
80
  pyinfra/facts/xbps.py,sha256=4gAajBlTAg3bo7vRdx3b2TTi-vvU1y86WZqC0H9nUUk,573
76
81
  pyinfra/facts/yum.py,sha256=i42g0FIZg62TZFqFcaUQWNekFFFo4G8vf5wyaKUuh8Q,938
77
- pyinfra/facts/zfs.py,sha256=MKqh1AEa5Bpa0KDT673e92TiOb8K7YHoEg-Xo424De8,1311
82
+ pyinfra/facts/zfs.py,sha256=YC9_-BnDkmhDtUUGWy2NpE-rlz5Xzl89uvHNCFpPSYM,1501
78
83
  pyinfra/facts/zypper.py,sha256=sAIZ5SqjsJ1Dc5e3pJrOoR5Gnu9BqZHpDFI8gKLts84,873
79
84
  pyinfra/facts/util/__init__.py,sha256=f7HKu8z9_yFC899ajJ3RFiyivioaZeGfOI6nf9GviCs,521
80
85
  pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
81
86
  pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGkrk,1181
87
+ pyinfra/facts/util/units.py,sha256=SNHCisxGwZedCOqO9tfOWJpZ5Stc0Wcg9mZcXoKBY0A,714
82
88
  pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
83
89
  pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
84
90
  pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
85
- pyinfra/operations/apt.py,sha256=YAVZXzCE5zvPQb6FMTcQVVKhdKHUKkV8Ra6fRhD8rYA,13887
91
+ pyinfra/operations/apt.py,sha256=OKAMTcvS83KwF9lCmLZ09swKezwD7GdIUJu6dGTfRao,14152
86
92
  pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
87
93
  pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
88
94
  pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
89
95
  pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
96
+ pyinfra/operations/crontab.py,sha256=qhYzj9xh-A6p95sJ0i_DDKOIm7WoNgiPjcR43ZB9iv8,6454
90
97
  pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
91
- pyinfra/operations/docker.py,sha256=RMkrVpS-eeN5zwGnpb3WeeOAoGvFma-A1aPvjE9M1KY,8336
92
- pyinfra/operations/files.py,sha256=iQVpI_7guyQRoc1hkMN2HrA1BbriuPvf5blHkhujA2I,54084
98
+ pyinfra/operations/docker.py,sha256=6jkeZT0JW8f5df2N3ANai4JOkGBnGd3nETE39_Ho7kM,8441
99
+ pyinfra/operations/files.py,sha256=BkDn4hTsic2T1r_0GAHz-r1Nxd6FZMWoTFqf8izu6VU,54707
93
100
  pyinfra/operations/flatpak.py,sha256=c2OAyuAvt3alVm9D8W6gCfmk5JFydcZD36gO_OhB8Bc,1891
94
101
  pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
95
- pyinfra/operations/git.py,sha256=b26tQF_4hykTy0FtxiuCkqPk9i8JdZaz-RBhH4X96yw,11789
102
+ pyinfra/operations/git.py,sha256=ajyusK8rFeU_u3piR3glzJGLhomKF0IuzlbR667eyts,13035
96
103
  pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
97
104
  pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
98
105
  pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
99
106
  pyinfra/operations/mysql.py,sha256=ctm2Z6MaB0mOArCNU4TsJzaXiKXQaa_ahmsC5Vvyi10,19857
100
107
  pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
101
108
  pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
109
+ pyinfra/operations/opkg.py,sha256=0P_YyXSvXUyVWJrHtnRxI_etya8yMIny7_LcgbQddEU,2627
102
110
  pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
103
- pyinfra/operations/pip.py,sha256=7PpQvZHnwBGZ60V5b0XKNR4tHLW0MXJo6_6UX7HBtGY,5856
111
+ pyinfra/operations/pip.py,sha256=MCmb9FPcyvg6M7lTlRtx2qpXHtyf-SwBVtHqcAkqVzQ,5925
112
+ pyinfra/operations/pipx.py,sha256=PFVXriRIk5gnJXWcFoghsCIVfTy7RvQyvDggjXHALQc,2188
104
113
  pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
105
114
  pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
106
- pyinfra/operations/postgres.py,sha256=eh3wjX-l4ri-q3mgfV2bdmVs3m87s3C1_EbJUFss9u4,9700
115
+ pyinfra/operations/postgres.py,sha256=qQZxTfMinl6PNR0l6Gt0dxsbnyUOZxQ2OIPi9UQh_cQ,9867
107
116
  pyinfra/operations/postgresql.py,sha256=agZjL2W4yxigk9ThIC0V_3wvmcWVdX308aJO24WkN6g,833
108
117
  pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,861
109
118
  pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
110
- pyinfra/operations/runit.py,sha256=jRR5kt1OUCLbYktnu7yl3YvSiTW51VvEvOuB0yfd7Ww,5126
119
+ pyinfra/operations/runit.py,sha256=-K0GhORE56J4Gjh7PCriSx9zZI7XJV-cIb-LnnSuKdY,5162
111
120
  pyinfra/operations/selinux.py,sha256=imZ4dbY4tl0GpBSkUgV983jbDDihWNs_OQkOBulT7FQ,5948
112
- pyinfra/operations/server.py,sha256=Fi5N53ZirbyybJzonQMIHsgI2CBLGGXTCNrr7IyyW2A,36567
121
+ pyinfra/operations/server.py,sha256=iz8r8MdufX6LAE6Ua4BJTIp6zPHsmY0DT-B6ifA-L9g,30700
113
122
  pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
114
123
  pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
115
124
  pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
@@ -118,7 +127,7 @@ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE
118
127
  pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
119
128
  pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
120
129
  pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
121
- pyinfra/operations/zfs.py,sha256=vQoeXcwtf-2WXLwBnkM77EhJ_9dQenPXRGcLW07l2_c,5227
130
+ pyinfra/operations/zfs.py,sha256=FnhiXreqf5qJBusC31dwrQsEi0MvH4qxzjBayHKFcYY,5283
122
131
  pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
123
132
  pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
124
133
  pyinfra/operations/util/docker.py,sha256=6CvQgeFAXH_lDqKb7RxWpMvlCDwEAXlBaDZoJ8LxrYg,4596
@@ -129,11 +138,11 @@ pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
129
138
  pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
130
139
  pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
131
140
  pyinfra_cli/exceptions.py,sha256=iptx9Zj1od7VgSbOyXs7P8tD4zAZ_fwrQFKPlpPrfS0,4806
132
- pyinfra_cli/inventory.py,sha256=vuSL7dU31hxazHmJoUI0c6QjdItG78x8O5ifLUWuMeI,11292
141
+ pyinfra_cli/inventory.py,sha256=05Z0LpMW8Qt3c2X2sx-roYCQGxNlEDD0RgZILpXCcJs,11768
133
142
  pyinfra_cli/log.py,sha256=7WEGtmf3ncF1BtXL2icUjyxeRKy-7XrCcQ2Hg4GWX5Y,2201
134
143
  pyinfra_cli/main.py,sha256=5VTniMcbKuIfjPTzaUklad5fM1BW7CUEARoSV9tPf1U,19954
135
- pyinfra_cli/prints.py,sha256=heCF-ugz0F8gTSr--rYVtRqN6jpAun5DUA4cy0F8l5A,9696
136
- pyinfra_cli/util.py,sha256=f3iGIPxlUiQJ5LmUGYbEz0QrySQAKmf9xov9WvHXbrk,6364
144
+ pyinfra_cli/prints.py,sha256=wVsTZgqEoL7Q_9dPMRoQXFZdYTIPjSGHhtGjjndEaXg,10291
145
+ pyinfra_cli/util.py,sha256=9ehdJQ8pDNBLHXoFst9p696VDT-b_qo8UFMJrqdE1rE,6424
137
146
  pyinfra_cli/virtualenv.py,sha256=6j9W54JkQLN02SrZZIVwszp0GxlaaDEUWFZjBDHIWNA,2466
138
147
  tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
148
  tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
@@ -148,10 +157,10 @@ tests/test_api/test_api_operations.py,sha256=GUfnuHK2NoTAGdOT4AbytT9R8i3ZZIvGP7K
148
157
  tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
149
158
  tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
159
  tests/test_cli/test_cli.py,sha256=IeWuhkhLzIkRbOEx5-yaW6xV5l4Y8fxaGaDGlMcOyYE,6016
151
- tests/test_cli/test_cli_deploy.py,sha256=3tlXpN_ntCvZDeymfQKrZm0kgADkiLUIAGQg5V8-KrU,5079
152
- tests/test_cli/test_cli_exceptions.py,sha256=02sjC6rMptuqchgcdjdsVNQbSQYW6HwGutSy6Q6sMs4,3088
153
- tests/test_cli/test_cli_inventory.py,sha256=xlo-p3HdfVPNqxi7SknEZ2mWrKsdDaK3PoVN-tl95Z0,2394
154
- tests/test_cli/test_cli_util.py,sha256=-Ehnj0cO-EkF-6KLxcPPcFeuAUMTz-fKITrxhuiYhV4,2562
160
+ tests/test_cli/test_cli_deploy.py,sha256=vZC7twj8sPCy05loO50P-D_Xf73r6XN4m7yVj7TIFmo,5243
161
+ tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
162
+ tests/test_cli/test_cli_inventory.py,sha256=feYSyxGBUsDGriOaqs0CBMbAIW7yUfoT1mktLwqMI0w,4036
163
+ tests/test_cli/test_cli_util.py,sha256=Fqn_9fnG6xNfw8OMHSZs9hQoW260aSt409S1bUAJs-Q,2500
155
164
  tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
156
165
  tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
157
166
  tests/test_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -160,13 +169,13 @@ tests/test_connectors/test_docker.py,sha256=0EjkfhCHpLCfL4X-AIdMNw5ASaseY0tbRAn7
160
169
  tests/test_connectors/test_dockerssh.py,sha256=MaC9IK1OZDiqoIsuLOZBJnPDglsMoPDoL19LQtXsyCE,9303
161
170
  tests/test_connectors/test_local.py,sha256=N_FkejDZKu7XLnKeApqfBARYMyxf-hRXCQJrXLHvwRg,7442
162
171
  tests/test_connectors/test_ssh.py,sha256=zYL0FbRXzqkYJslhmVeUgSkcHtozhmvZfRcaqDrYKvI,40386
163
- tests/test_connectors/test_sshuserclient.py,sha256=2PQNLPhNL6lBACc6tQuXmPoog-9L6AdDQNrA-rEw1_8,5734
172
+ tests/test_connectors/test_sshuserclient.py,sha256=Rm1zxSODDXQXuQ3qdkCA12FCUj7Zkgwwe-s2UrsZAoE,8599
164
173
  tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
165
174
  tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
166
175
  tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
167
- pyinfra-3.1.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
168
- pyinfra-3.1.dist-info/METADATA,sha256=NCTtirew0Vm6uxlBVYrUz6b1aVFoJxHGCV9_MzCS6n0,8039
169
- pyinfra-3.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
170
- pyinfra-3.1.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
171
- pyinfra-3.1.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
172
- pyinfra-3.1.dist-info/RECORD,,
176
+ pyinfra-3.2.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
177
+ pyinfra-3.2.dist-info/METADATA,sha256=edyD-006yB75ALtE2Hz3_Oqj4DLFUJjEMvfs7fwQw44,8011
178
+ pyinfra-3.2.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
179
+ pyinfra-3.2.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
180
+ pyinfra-3.2.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
181
+ pyinfra-3.2.dist-info/RECORD,,
pyinfra_cli/inventory.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import socket
2
2
  from collections import defaultdict
3
3
  from os import listdir, path
4
- from types import GeneratorType
5
4
  from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
6
5
 
7
6
  from pyinfra import logger
@@ -23,18 +22,37 @@ def _is_inventory_group(key: str, value: Any):
23
22
  Verify that a module-level variable (key = value) is a valid inventory group.
24
23
  """
25
24
 
26
- if key.startswith("_") or not isinstance(value, (list, tuple, GeneratorType)):
25
+ if key.startswith("__"):
26
+ # Ignore __builtins__/__file__
27
+ return False
28
+ elif key.startswith("_"):
29
+ logger.debug(
30
+ 'Ignoring variable "%s" in inventory file since it starts with a leading underscore',
31
+ key,
32
+ )
27
33
  return False
28
34
 
29
- # If the group is a tuple of (hosts, data), check the hosts
30
- if isinstance(value, tuple):
35
+ if isinstance(value, list):
36
+ pass
37
+ elif isinstance(value, tuple):
38
+ # If the group is a tuple of (hosts, data), check the hosts
31
39
  value = value[0]
40
+ else:
41
+ logger.debug(
42
+ 'Ignoring variable "%s" in inventory file since it is not a list or tuple',
43
+ key,
44
+ )
45
+ return False
32
46
 
33
- # Expand any generators of hosts
34
- if isinstance(value, GeneratorType):
35
- value = list(value)
47
+ if not all(isinstance(item, ALLOWED_HOST_TYPES) for item in value):
48
+ logger.warning(
49
+ 'Ignoring host group "%s". '
50
+ "Host groups may only contain strings (host) or tuples (host, data).",
51
+ key,
52
+ )
53
+ return False
36
54
 
37
- return all(isinstance(item, ALLOWED_HOST_TYPES) for item in value)
55
+ return True
38
56
 
39
57
 
40
58
  def _get_group_data(dirname_or_filename: str):
@@ -258,7 +276,6 @@ def make_inventory_from_files(
258
276
  for hosts in groups.values():
259
277
  # Groups can be a list of hosts or tuple of (hosts, data)
260
278
  hosts = _get_any_tuple_first(hosts)
261
-
262
279
  for host in hosts:
263
280
  # Hosts can be a hostname or tuple of (hostname, data)
264
281
  hostname = _get_any_tuple_first(host)