pyinfra 3.1.1__py2.py3-none-any.whl → 3.3__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 (104) hide show
  1. pyinfra/api/arguments.py +9 -2
  2. pyinfra/api/arguments_typed.py +4 -5
  3. pyinfra/api/command.py +22 -3
  4. pyinfra/api/config.py +5 -2
  5. pyinfra/api/deploy.py +4 -2
  6. pyinfra/api/facts.py +3 -0
  7. pyinfra/api/host.py +15 -7
  8. pyinfra/api/operation.py +2 -1
  9. pyinfra/api/state.py +1 -1
  10. pyinfra/connectors/base.py +34 -8
  11. pyinfra/connectors/chroot.py +7 -2
  12. pyinfra/connectors/docker.py +24 -8
  13. pyinfra/connectors/dockerssh.py +7 -2
  14. pyinfra/connectors/local.py +7 -2
  15. pyinfra/connectors/ssh.py +9 -2
  16. pyinfra/connectors/sshuserclient/client.py +42 -14
  17. pyinfra/connectors/sshuserclient/config.py +2 -0
  18. pyinfra/connectors/terraform.py +1 -1
  19. pyinfra/connectors/util.py +13 -9
  20. pyinfra/context.py +9 -2
  21. pyinfra/facts/apk.py +8 -1
  22. pyinfra/facts/apt.py +68 -0
  23. pyinfra/facts/brew.py +13 -0
  24. pyinfra/facts/bsdinit.py +3 -0
  25. pyinfra/facts/cargo.py +5 -0
  26. pyinfra/facts/choco.py +6 -0
  27. pyinfra/facts/crontab.py +195 -0
  28. pyinfra/facts/deb.py +10 -0
  29. pyinfra/facts/dnf.py +5 -0
  30. pyinfra/facts/docker.py +16 -0
  31. pyinfra/facts/efibootmgr.py +113 -0
  32. pyinfra/facts/files.py +112 -7
  33. pyinfra/facts/flatpak.py +7 -0
  34. pyinfra/facts/freebsd.py +75 -0
  35. pyinfra/facts/gem.py +5 -0
  36. pyinfra/facts/git.py +12 -2
  37. pyinfra/facts/gpg.py +7 -0
  38. pyinfra/facts/hardware.py +13 -0
  39. pyinfra/facts/iptables.py +9 -1
  40. pyinfra/facts/launchd.py +5 -0
  41. pyinfra/facts/lxd.py +5 -0
  42. pyinfra/facts/mysql.py +9 -2
  43. pyinfra/facts/npm.py +5 -0
  44. pyinfra/facts/openrc.py +8 -0
  45. pyinfra/facts/opkg.py +245 -0
  46. pyinfra/facts/pacman.py +9 -1
  47. pyinfra/facts/pip.py +5 -0
  48. pyinfra/facts/pipx.py +82 -0
  49. pyinfra/facts/pkg.py +4 -0
  50. pyinfra/facts/pkgin.py +5 -0
  51. pyinfra/facts/podman.py +54 -0
  52. pyinfra/facts/postgres.py +10 -2
  53. pyinfra/facts/rpm.py +11 -0
  54. pyinfra/facts/runit.py +7 -0
  55. pyinfra/facts/selinux.py +16 -0
  56. pyinfra/facts/server.py +87 -79
  57. pyinfra/facts/snap.py +7 -0
  58. pyinfra/facts/systemd.py +5 -0
  59. pyinfra/facts/sysvinit.py +4 -0
  60. pyinfra/facts/upstart.py +5 -0
  61. pyinfra/facts/util/__init__.py +4 -1
  62. pyinfra/facts/util/units.py +30 -0
  63. pyinfra/facts/vzctl.py +5 -0
  64. pyinfra/facts/xbps.py +6 -1
  65. pyinfra/facts/yum.py +5 -0
  66. pyinfra/facts/zfs.py +41 -21
  67. pyinfra/facts/zypper.py +5 -0
  68. pyinfra/local.py +3 -2
  69. pyinfra/operations/apt.py +36 -22
  70. pyinfra/operations/crontab.py +189 -0
  71. pyinfra/operations/docker.py +61 -56
  72. pyinfra/operations/files.py +65 -1
  73. pyinfra/operations/freebsd/__init__.py +12 -0
  74. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  75. pyinfra/operations/freebsd/pkg.py +219 -0
  76. pyinfra/operations/freebsd/service.py +116 -0
  77. pyinfra/operations/freebsd/sysrc.py +92 -0
  78. pyinfra/operations/git.py +23 -7
  79. pyinfra/operations/opkg.py +88 -0
  80. pyinfra/operations/pip.py +3 -2
  81. pyinfra/operations/pipx.py +90 -0
  82. pyinfra/operations/postgres.py +114 -27
  83. pyinfra/operations/runit.py +2 -0
  84. pyinfra/operations/server.py +9 -181
  85. pyinfra/operations/util/docker.py +44 -22
  86. pyinfra/operations/zfs.py +3 -3
  87. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/LICENSE.md +1 -1
  88. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/METADATA +25 -25
  89. pyinfra-3.3.dist-info/RECORD +187 -0
  90. pyinfra_cli/exceptions.py +5 -0
  91. pyinfra_cli/inventory.py +26 -9
  92. pyinfra_cli/log.py +3 -0
  93. pyinfra_cli/main.py +9 -8
  94. pyinfra_cli/prints.py +19 -4
  95. pyinfra_cli/util.py +3 -0
  96. pyinfra_cli/virtualenv.py +1 -1
  97. tests/test_cli/test_cli_deploy.py +15 -13
  98. tests/test_cli/test_cli_inventory.py +53 -0
  99. tests/test_connectors/test_ssh.py +302 -182
  100. tests/test_connectors/test_sshuserclient.py +68 -1
  101. pyinfra-3.1.1.dist-info/RECORD +0 -172
  102. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/WHEEL +0 -0
  103. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/entry_points.txt +0 -0
  104. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/top_level.txt +0 -0
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()
@@ -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
 
@@ -103,7 +103,7 @@ def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extr
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)
@@ -1,4 +1,4 @@
1
- Copyright (C) 2020 Nick Barrett <nick@fizzadar.com>
1
+ Copyright (C) 2025 Nick Barrett <nick@fizzadar.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1.1
3
+ Version: 3.3
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -16,7 +16,6 @@ Classifier: Intended Audience :: Information Technology
16
16
  Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
19
  Classifier: Programming Language :: Python :: 3.9
21
20
  Classifier: Programming Language :: Python :: 3.10
22
21
  Classifier: Programming Language :: Python :: 3.11
@@ -24,7 +23,7 @@ Classifier: Programming Language :: Python :: 3.12
24
23
  Classifier: Topic :: System :: Systems Administration
25
24
  Classifier: Topic :: System :: Installation/Setup
26
25
  Classifier: Topic :: Utilities
27
- Requires-Python: >=3.8
26
+ Requires-Python: >=3.9
28
27
  Description-Content-Type: text/markdown
29
28
  License-File: LICENSE.md
30
29
  Requires-Dist: gevent >=1.5
@@ -33,7 +32,6 @@ Requires-Dist: click >2
33
32
  Requires-Dist: jinja2 <4,>2
34
33
  Requires-Dist: python-dateutil <3,>2
35
34
  Requires-Dist: setuptools
36
- Requires-Dist: configparser
37
35
  Requires-Dist: pywinrm
38
36
  Requires-Dist: typeguard
39
37
  Requires-Dist: distro <2,>=1.6
@@ -42,43 +40,45 @@ Requires-Dist: importlib-metadata >=3.6 ; python_version < "3.10"
42
40
  Requires-Dist: typing-extensions ; python_version < "3.11"
43
41
  Requires-Dist: graphlib-backport ; python_version < "3.9"
44
42
  Provides-Extra: dev
45
- Requires-Dist: pytest ==8.2.1 ; extra == 'dev'
46
- Requires-Dist: coverage ==7.5.1 ; extra == 'dev'
47
- Requires-Dist: pytest-cov ==5.0.0 ; extra == 'dev'
48
- Requires-Dist: black ==24.4.2 ; extra == 'dev'
49
- Requires-Dist: isort ==5.13.2 ; extra == 'dev'
50
- Requires-Dist: flake8 ==7.0.0 ; extra == 'dev'
43
+ Requires-Dist: pytest ==8.3.5 ; extra == 'dev'
44
+ Requires-Dist: coverage ==7.7.1 ; extra == 'dev'
45
+ Requires-Dist: pytest-cov ==6.0.0 ; extra == 'dev'
46
+ Requires-Dist: black ==25.1.0 ; extra == 'dev'
47
+ Requires-Dist: isort ==6.0.1 ; extra == 'dev'
48
+ Requires-Dist: flake8 ==7.1.2 ; extra == 'dev'
51
49
  Requires-Dist: flake8-black ==0.3.6 ; extra == 'dev'
52
- Requires-Dist: flake8-isort ==6.1.1 ; extra == 'dev'
50
+ Requires-Dist: flake8-isort ==6.1.2 ; extra == 'dev'
51
+ Requires-Dist: pyyaml ==6.0.2 ; extra == 'dev'
53
52
  Requires-Dist: mypy ; extra == 'dev'
54
53
  Requires-Dist: types-cryptography ; extra == 'dev'
55
54
  Requires-Dist: types-paramiko ; extra == 'dev'
56
55
  Requires-Dist: types-python-dateutil ; extra == 'dev'
57
56
  Requires-Dist: types-PyYAML ; extra == 'dev'
58
57
  Requires-Dist: types-setuptools ; extra == 'dev'
59
- Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'dev'
60
- Requires-Dist: myst-parser ==2.0.0 ; extra == 'dev'
61
- Requires-Dist: sphinx ==6.2.1 ; extra == 'dev'
58
+ Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.17 ; extra == 'dev'
59
+ Requires-Dist: myst-parser ==4.0.1 ; extra == 'dev'
60
+ Requires-Dist: sphinx ==8.2.3 ; extra == 'dev'
62
61
  Requires-Dist: wheel ; extra == 'dev'
63
62
  Requires-Dist: twine ; extra == 'dev'
64
63
  Requires-Dist: ipython ; extra == 'dev'
65
64
  Requires-Dist: ipdb ; extra == 'dev'
66
65
  Requires-Dist: ipdbplugin ; extra == 'dev'
67
- Requires-Dist: flake8-spellcheck ==0.12.1 ; extra == 'dev'
66
+ Requires-Dist: flake8-spellcheck ==0.28.0 ; extra == 'dev'
68
67
  Requires-Dist: redbaron ; extra == 'dev'
69
68
  Provides-Extra: docs
70
- Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'docs'
71
- Requires-Dist: myst-parser ==2.0.0 ; extra == 'docs'
72
- Requires-Dist: sphinx ==6.2.1 ; extra == 'docs'
69
+ Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.17 ; extra == 'docs'
70
+ Requires-Dist: myst-parser ==4.0.1 ; extra == 'docs'
71
+ Requires-Dist: sphinx ==8.2.3 ; 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'
76
- Requires-Dist: pytest-cov ==5.0.0 ; extra == 'test'
77
- Requires-Dist: black ==24.4.2 ; extra == 'test'
78
- Requires-Dist: isort ==5.13.2 ; extra == 'test'
79
- Requires-Dist: flake8 ==7.0.0 ; extra == 'test'
73
+ Requires-Dist: pytest ==8.3.5 ; extra == 'test'
74
+ Requires-Dist: coverage ==7.7.1 ; extra == 'test'
75
+ Requires-Dist: pytest-cov ==6.0.0 ; extra == 'test'
76
+ Requires-Dist: black ==25.1.0 ; extra == 'test'
77
+ Requires-Dist: isort ==6.0.1 ; extra == 'test'
78
+ Requires-Dist: flake8 ==7.1.2 ; extra == 'test'
80
79
  Requires-Dist: flake8-black ==0.3.6 ; extra == 'test'
81
- Requires-Dist: flake8-isort ==6.1.1 ; extra == 'test'
80
+ Requires-Dist: flake8-isort ==6.1.2 ; extra == 'test'
81
+ Requires-Dist: pyyaml ==6.0.2 ; extra == 'test'
82
82
  Requires-Dist: mypy ; extra == 'test'
83
83
  Requires-Dist: types-cryptography ; extra == 'test'
84
84
  Requires-Dist: types-paramiko ; extra == 'test'
@@ -0,0 +1,187 @@
1
+ pyinfra/__init__.py,sha256=7ZcKHGWk7_nYxsYrbFBB_vJr-J-Ddbc56ZS4sk5ArVw,535
2
+ pyinfra/__main__.py,sha256=aVd00glLz5CMJGXgt1XxbOvC2HluqaowoTOjxgIpBaA,47
3
+ pyinfra/context.py,sha256=CLUKJxvZm3VGtxMp6OlZnhYvZBGevSnJYemSDhQRd6E,3534
4
+ pyinfra/local.py,sha256=wT84xkJc9UBN5isvIVbNpm2fzZaadwE-dkbAwaFQdZk,2808
5
+ pyinfra/progress.py,sha256=X3hXZ4Flh_L9FE4ZEWxWoG0R4dA5UPd1FCO-Exd5Xtc,4193
6
+ pyinfra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ pyinfra/version.py,sha256=LZf50PHDzEZv65w0G-iMICoQ9US0U5LWHAOEmNtkF3I,216
8
+ pyinfra/api/__init__.py,sha256=suGbKKM-qCduuXFYBEcyswlTqozewtYpdLRhK63PVn0,942
9
+ pyinfra/api/arguments.py,sha256=GdkwOpBOUKCDer6gxXtChKmRD8iDr_1pQMXXv7jzfDw,10266
10
+ pyinfra/api/arguments_typed.py,sha256=IZuhpmDfW9CP6ASS5Ie-3Wcnxl6bDNR3egU4Mfhbsb4,2303
11
+ pyinfra/api/command.py,sha256=NwF2syxV3zxCbBdHzvJ6ve5G-xwdNTjPHFPwguKVcYs,7741
12
+ pyinfra/api/config.py,sha256=LhmMKOoAaawSsrSnl55BBj63oT-U-Wn8nwgbe43kzd0,9113
13
+ pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
14
+ pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
15
+ pyinfra/api/deploy.py,sha256=CllKp1QYLseeMStj35waZazB0hUuy3iGvDt7b_lH31M,3138
16
+ pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
17
+ pyinfra/api/facts.py,sha256=CoUtBfn5_O9oFHqwVEghQftH-Hc2XUBJI8LCEpn0nPc,9516
18
+ pyinfra/api/host.py,sha256=KZr2ZvcCM3asrFkpKL25tQIGCr8lZFKTh1aoC2wIpyE,14002
19
+ pyinfra/api/inventory.py,sha256=nPITdNEJ7q71adIqS_OKHsMjD7amUuHEuTl6xzgh1Gk,7734
20
+ pyinfra/api/operation.py,sha256=tyslPCFmHDGydxf0Sv-QfDq1DzGGul9oOgKCXYEfusM,15194
21
+ pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
22
+ pyinfra/api/state.py,sha256=IqdWT1Uwf_Rd5piqYLrf7y8HsXPs1hY6wCmxoRGqPTE,12630
23
+ pyinfra/api/util.py,sha256=K4aFjGW7KAz2ZQqfRriRqyHMCQFFrX06WPola3epjaE,12410
24
+ pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ pyinfra/connectors/base.py,sha256=0-BKInwTpjbnTakJhMTn_8LUOl81vUmC-q-HzVrwhkw,4703
26
+ pyinfra/connectors/chroot.py,sha256=sQIM1nOLzyIvv7FsqgAZsZ71jJtWLAQB_uaeB4m4GqE,6007
27
+ pyinfra/connectors/docker.py,sha256=bPbw9g9y5-WreD9drTRbDJ1x-CKuO-hgIDUZxuGc86s,9284
28
+ pyinfra/connectors/dockerssh.py,sha256=sJxnmZ-xG19C9BSw6jx54zJhM3PH5ucmdTCZR2ywbrk,9007
29
+ pyinfra/connectors/local.py,sha256=E57xCdBibo2M0IJQ7o6V5nTeBCCn_Bg6vmhydDhHagI,6976
30
+ pyinfra/connectors/ssh.py,sha256=RF_0R8Vy8MFO8A6Eq-F3VjvZXVVWiN7dXVKaWt_5cww,21328
31
+ pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
+ pyinfra/connectors/terraform.py,sha256=92KwojQKQhqRYuX3UE9ANRvj0Diwg3EEi6blTgu_LPk,4235
33
+ pyinfra/connectors/util.py,sha256=Up7kToERfcyjoayY9K6-Xdwjg4-SO2THAy7jzzcznJ8,11651
34
+ pyinfra/connectors/vagrant.py,sha256=oEeRglzRmemRXW3vilsp_Xg9qnZMRprRJO9fd_C-f5M,4759
35
+ pyinfra/connectors/sshuserclient/__init__.py,sha256=Qc4RO2wknSWIiNTwOeQ0y2TeiuKHmyWDW2Dz4MOo9CE,44
36
+ pyinfra/connectors/sshuserclient/client.py,sha256=z4FpWJ9zX3TNdb1SM4g53m5XByADVSyu2A_oPcalj3I,10489
37
+ pyinfra/connectors/sshuserclient/config.py,sha256=FZkPrUYXkURZcFUHBGWw9lLC9uiH3DJ0rBYXJePchxw,2774
38
+ pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
39
+ pyinfra/facts/apk.py,sha256=UEMHzhx2Wx3qq-OcjetWgE2iZ7_EjI-bszLxSN6PJa0,799
40
+ pyinfra/facts/apt.py,sha256=RYPqGgdH0QpDIoMNhBY9nKtMu-baQNX4DIYNrsllcLQ,4026
41
+ pyinfra/facts/brew.py,sha256=vkl3tr9U6gyx3UDZzanz3WXmvpZ3HqBpUGUxkxdnjU4,2778
42
+ pyinfra/facts/bsdinit.py,sha256=SVY4hagjyy1yz8FKWhIbX9fHm5AugvTFl4xQh2FFO74,631
43
+ pyinfra/facts/cargo.py,sha256=qgOClhwZm4COcncDzOZccCzs67nPBi_x6VGiF2UA0sA,687
44
+ pyinfra/facts/choco.py,sha256=mpLleSqNqiaGRgyrhgceno2iPB1_1yjn8UJ90pvOZCs,886
45
+ pyinfra/facts/crontab.py,sha256=yCpBzBqgXt2BjAGCIttuQ2xKKtuhzg0VQ9WCDV46eV8,5754
46
+ pyinfra/facts/deb.py,sha256=1dR1puwY5wyyhhYYwaEBLjKU9sIyaNBNBlamVZ2KQg0,2074
47
+ pyinfra/facts/dnf.py,sha256=IFOJWeXMLYxSZEgleOCMZaBIZOzJzklwCmemZIUwDmk,1059
48
+ pyinfra/facts/docker.py,sha256=KCHz0onAHXOtMlF8ljG7AOrDS57ajhE5RJ34qnTT1VA,2626
49
+ pyinfra/facts/efibootmgr.py,sha256=JPJSokE_RV9JstEPJRECnqSU-B0JCxmrocY8zBOva7M,3555
50
+ pyinfra/facts/files.py,sha256=bnu3JSoWRdOR1Uxv9nBn1DbanBeF3kWet_rc-3Aj63s,15363
51
+ pyinfra/facts/flatpak.py,sha256=ovi3duwTqqwvt5GoDRN7R-PpkvR6sQ1SmgEADcSnkUE,1646
52
+ pyinfra/facts/freebsd.py,sha256=z7ZJRK8NV5HL-BfAdQs6pQKLEdrfdv7dVUmmOtVKUbA,2248
53
+ pyinfra/facts/gem.py,sha256=aX2-vcEqkxUIP0UJ_SVp9bf4B944oyDjsuujjs5q_9w,654
54
+ pyinfra/facts/git.py,sha256=os8zA2F8UbdX444VUedfL3zYFN_sKsumMjEo-H3isc4,1485
55
+ pyinfra/facts/gpg.py,sha256=8fvC9AglzmO08SAT_Py6H6G0i73soO1D2cSCbw-O9JA,3923
56
+ pyinfra/facts/hardware.py,sha256=JKyz3gZxSXPcVaMuH7iKHOww5a4kbZTiLIQbNmvkF2Q,12217
57
+ pyinfra/facts/iptables.py,sha256=ORRExNnPR5ysymDbkUjeQWt9mzjSI7su4uQfsu3FlJQ,3506
58
+ pyinfra/facts/launchd.py,sha256=Nl4EFbGwlXTj8GNWlS9UMZODHr63bve73xmMeqyf2gc,849
59
+ pyinfra/facts/lxd.py,sha256=A2LQN5armY_nI6gV-m1j4l2J2_mMZnvVa1X2RntDnDk,548
60
+ pyinfra/facts/mysql.py,sha256=ZVN6eN0Xetidvq_RAntSCzHgy4jmf8Enrm2mPXxO488,6247
61
+ pyinfra/facts/npm.py,sha256=WzZHCfgWwY9WsPPzL0njlazwiBHI0gpvyZV93Nfu428,838
62
+ pyinfra/facts/openrc.py,sha256=wB4fjzgWCgX6wexBm4jlWv4YH6b4z5_tAqQAO1qO9Kg,1614
63
+ pyinfra/facts/opkg.py,sha256=o11yGcox2q5O8K_pvCxPqNywiV2lET9WJrgkeCU0Ahs,7390
64
+ pyinfra/facts/pacman.py,sha256=SPI3jpzZbq_VnJ0z5q7A2Rw___EHVntOz6M19ErGpmQ,1324
65
+ pyinfra/facts/pip.py,sha256=MA2yed2_kVzLNjCulnCJEz4GeQ5dbGVtaAv4Bqm9fLw,820
66
+ pyinfra/facts/pipx.py,sha256=3-oP0yHWvi__RGRlgkQzRKQBMwZN3Ke9A-z8oYB5upM,1854
67
+ pyinfra/facts/pkg.py,sha256=4qcp0iO3TgWL9zvPCXcZDCRwrbWg65Wg8H0OvWqNqJk,588
68
+ pyinfra/facts/pkgin.py,sha256=ErtNMLn-Si6_nA69V1x_dZOX_Y_z5CkkevctXq0Nj-w,662
69
+ pyinfra/facts/podman.py,sha256=ONDyCaWe3k_BLO8ec0_KLHxrVgpUKLpKhuJ2bdrEuuo,1201
70
+ pyinfra/facts/postgres.py,sha256=TPcrYc9IhVFNW_rltc1oFUk4nfF0iWlS4gZMv0w7pXM,4499
71
+ pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
72
+ pyinfra/facts/rpm.py,sha256=ikuKYiUmjgvPA84qfE-gbq4Iv4AB5cvor1uKU6uHbXQ,2391
73
+ pyinfra/facts/runit.py,sha256=qok1FTSshiNrN603vjYTKOeM-NIlxwLbwOp-vPbPySo,2131
74
+ pyinfra/facts/selinux.py,sha256=8OylQ3H-huktRS34g5FYwivlp1wb4mKP0EFVf1LtMEE,4679
75
+ pyinfra/facts/server.py,sha256=peaK4q2MGhFoKkQGTJ20rU1P9_YPrLQKNPmmw2VqYbA,19678
76
+ pyinfra/facts/snap.py,sha256=2-c3z1qpOG7prmKJssLpOXmKo_wwdfROryro6gif2vo,2137
77
+ pyinfra/facts/systemd.py,sha256=ua5ACLINcCwXv39gnU6HJuIYhQB896yi6-M6hSuUtsU,4309
78
+ pyinfra/facts/sysvinit.py,sha256=q1OpHATFJxjLwatcnYRfpTR7_K2c29b4ppmZu-wgC-4,1589
79
+ pyinfra/facts/upstart.py,sha256=GcreN0mIM6_qRgqzFaA7PnX45RtbBpvVC00J6bKujyA,717
80
+ pyinfra/facts/vzctl.py,sha256=lUacmyakn2sJ2tD2FDAh1eeX3sxEVq7aRRwWM4QTguQ,760
81
+ pyinfra/facts/xbps.py,sha256=pNpgeITdHoJWhnJ_XFjySJ7H35d9h_v2F7GKqIrxgt0,663
82
+ pyinfra/facts/yum.py,sha256=OE1CJrX0AFOHuOdoPMfZuqUQ8hMLgJPtZGCzC9HH4Ho,1020
83
+ pyinfra/facts/zfs.py,sha256=4cWfTu2_V3Rkku8LfWzwruP_Tu4gJV2ZOrtW3otbP2w,1805
84
+ pyinfra/facts/zypper.py,sha256=RRSGrWAfgyiFVQAesjKpYf6i3M2d2Fagdb69BSF0iEM,955
85
+ pyinfra/facts/util/__init__.py,sha256=FNqUZjHPzJplb6ctHdZCVvmxAeVDQfFYmghC6B5edWQ,573
86
+ pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
87
+ pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGkrk,1181
88
+ pyinfra/facts/util/units.py,sha256=SNHCisxGwZedCOqO9tfOWJpZ5Stc0Wcg9mZcXoKBY0A,714
89
+ pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
90
+ pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
91
+ pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
92
+ pyinfra/operations/apt.py,sha256=w8rb8_yH4gKrdhDLKfWcziC5XQRXfhcGqNUwVcqrXpk,14352
93
+ pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
94
+ pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
95
+ pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
96
+ pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
97
+ pyinfra/operations/crontab.py,sha256=qhYzj9xh-A6p95sJ0i_DDKOIm7WoNgiPjcR43ZB9iv8,6454
98
+ pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
99
+ pyinfra/operations/docker.py,sha256=lU0AQqfIQYPHniruu770ClQODUSMuPVhFdIKy9gGmbw,8455
100
+ pyinfra/operations/files.py,sha256=Hs_HKU2STE0Byt-fZHdsfcrhM6m82mzAELMA22b_X_c,56215
101
+ pyinfra/operations/flatpak.py,sha256=c2OAyuAvt3alVm9D8W6gCfmk5JFydcZD36gO_OhB8Bc,1891
102
+ pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
103
+ pyinfra/operations/git.py,sha256=ajyusK8rFeU_u3piR3glzJGLhomKF0IuzlbR667eyts,13035
104
+ pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
105
+ pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
106
+ pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
107
+ pyinfra/operations/mysql.py,sha256=ctm2Z6MaB0mOArCNU4TsJzaXiKXQaa_ahmsC5Vvyi10,19857
108
+ pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
109
+ pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
110
+ pyinfra/operations/opkg.py,sha256=xBdmU07MWr-YBKX0EV0GuVrwMy6MOOf7wzYvD42sF_I,2607
111
+ pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
112
+ pyinfra/operations/pip.py,sha256=MCmb9FPcyvg6M7lTlRtx2qpXHtyf-SwBVtHqcAkqVzQ,5925
113
+ pyinfra/operations/pipx.py,sha256=PFVXriRIk5gnJXWcFoghsCIVfTy7RvQyvDggjXHALQc,2188
114
+ pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
115
+ pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
116
+ pyinfra/operations/postgres.py,sha256=hD65hRb8s3h6zlG-9WwT4JsNEXuxJUCY8ZRX61qlvlI,13367
117
+ pyinfra/operations/postgresql.py,sha256=agZjL2W4yxigk9ThIC0V_3wvmcWVdX308aJO24WkN6g,833
118
+ pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,861
119
+ pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
120
+ pyinfra/operations/runit.py,sha256=-K0GhORE56J4Gjh7PCriSx9zZI7XJV-cIb-LnnSuKdY,5162
121
+ pyinfra/operations/selinux.py,sha256=imZ4dbY4tl0GpBSkUgV983jbDDihWNs_OQkOBulT7FQ,5948
122
+ pyinfra/operations/server.py,sha256=6OkfbEH5jWltXjiQkXoq_82UidHPB551dL6TyxCfzr8,30733
123
+ pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
124
+ pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
125
+ pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
126
+ pyinfra/operations/sysvinit.py,sha256=WzzthkmWL46MNNY6LsBZ90e37yPj0w2QyUtEAlGBwqY,4078
127
+ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE,1978
128
+ pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
129
+ pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
130
+ pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
131
+ pyinfra/operations/zfs.py,sha256=FnhiXreqf5qJBusC31dwrQsEi0MvH4qxzjBayHKFcYY,5283
132
+ pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
133
+ pyinfra/operations/freebsd/__init__.py,sha256=PXsCLQG29VhimYw4uwwB2FcDt6CrofOtD3LTHC-ik-8,362
134
+ pyinfra/operations/freebsd/freebsd_update.py,sha256=lH0wB_i6DkaXQnDPjsDq8ocO1hYHL0v_a1go4exYcR8,1864
135
+ pyinfra/operations/freebsd/pkg.py,sha256=3AyfI0-_9F4ho47KqZsOMQocwNtTF2q9g0i6TffJVak,4413
136
+ pyinfra/operations/freebsd/service.py,sha256=1f7nTHELnhs3HBSrMFsmopVgYFMIwB8Se88yneRQ8Rw,3198
137
+ pyinfra/operations/freebsd/sysrc.py,sha256=eg7u_JsCge_uKq3Ysc_mohUc6qgJrOZStp_B_l2Hav4,2330
138
+ pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
139
+ pyinfra/operations/util/docker.py,sha256=WUyn2BZGClBwQDNTNNM3UNOX64YkpD4JMGk3lBufMTg,5285
140
+ pyinfra/operations/util/files.py,sha256=Zcet3ydNVbdT9jss0BDm6RJFyR_s6XTr0isDR60Zubw,3622
141
+ pyinfra/operations/util/packaging.py,sha256=xFtOlEX46ms7g3gDvOOInRVR1RVfgsmhLzFzsJAL_eU,9381
142
+ pyinfra/operations/util/service.py,sha256=kJd1zj4-sAaGIp5Ts7yAJznogWaGr8oQTztwenLAr7Y,1309
143
+ pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
144
+ pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
145
+ pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
146
+ pyinfra_cli/exceptions.py,sha256=RRaOprL7SmVv--FLy4x7fxeTitx9wYI0Y3_h01LfhJA,4901
147
+ pyinfra_cli/inventory.py,sha256=05Z0LpMW8Qt3c2X2sx-roYCQGxNlEDD0RgZILpXCcJs,11768
148
+ pyinfra_cli/log.py,sha256=mD96MH2owQQ5AsYRw7osCKENdp-E3Wum5IDr6qhSIa4,2268
149
+ pyinfra_cli/main.py,sha256=PyofivS-dRF8Sfq-PIy7_GrJKKAdmucZ0IbtkNlcHy4,20085
150
+ pyinfra_cli/prints.py,sha256=eaLRQAdiVGmPR2GsHF_fCY706aSt5JyXxz42z6qLAFM,10299
151
+ pyinfra_cli/util.py,sha256=9ehdJQ8pDNBLHXoFst9p696VDT-b_qo8UFMJrqdE1rE,6424
152
+ pyinfra_cli/virtualenv.py,sha256=wRNxOPcUkbD_Pzuj-Lnrz1KxGmsLlb2ObmCTFrdD-S8,2474
153
+ tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
+ tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
155
+ tests/test_api/test_api_arguments.py,sha256=5k7w0_x5cnABEFOk0LQBCt5gU9iTN9lo2XS6MlJTnhQ,1961
156
+ tests/test_api/test_api_command.py,sha256=OW0ESMyS5vo38u17DHeCrSIaIkW9gMU5PSkXL7mRrq0,3204
157
+ tests/test_api/test_api_config.py,sha256=bf0mDrUie3On6zGC_hJBpv-wvSf3LHBIBzUDvkopEt0,708
158
+ tests/test_api/test_api_deploys.py,sha256=h_zbI6CK4K8SdzEr3LEAMPxOf9hnQBdi_suqiNPqHHQ,4200
159
+ tests/test_api/test_api_facts.py,sha256=WnKwgLq7sk2LNO5IgIZbO5HRkDr-2GdUWO_EFfTjhO8,10695
160
+ tests/test_api/test_api_host.py,sha256=U_VW2vTl35vR8EdyIGMKr4y0ydsDLbvHSjZDa99CyNE,1119
161
+ tests/test_api/test_api_inventory.py,sha256=rqXd3e_Wwc-SxCzxgR5eLd7ZOdrF8CcHbcTZndLy5gE,2744
162
+ tests/test_api/test_api_operations.py,sha256=GUfnuHK2NoTAGdOT4AbytT9R8i3ZZIvGP7KBfoYcYUQ,20134
163
+ tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
164
+ tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
+ tests/test_cli/test_cli.py,sha256=IeWuhkhLzIkRbOEx5-yaW6xV5l4Y8fxaGaDGlMcOyYE,6016
166
+ tests/test_cli/test_cli_deploy.py,sha256=vZC7twj8sPCy05loO50P-D_Xf73r6XN4m7yVj7TIFmo,5243
167
+ tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
168
+ tests/test_cli/test_cli_inventory.py,sha256=feYSyxGBUsDGriOaqs0CBMbAIW7yUfoT1mktLwqMI0w,4036
169
+ tests/test_cli/test_cli_util.py,sha256=Fqn_9fnG6xNfw8OMHSZs9hQoW260aSt409S1bUAJs-Q,2500
170
+ tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
171
+ tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
172
+ tests/test_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
+ tests/test_connectors/test_chroot.py,sha256=QK7YgFPXzHh8y363-tmHvzZ0Ok5PVJWFTDAvwt91eac,5907
174
+ tests/test_connectors/test_docker.py,sha256=0EjkfhCHpLCfL4X-AIdMNw5ASaseY0tbRAn7T_TAkMQ,6566
175
+ tests/test_connectors/test_dockerssh.py,sha256=MaC9IK1OZDiqoIsuLOZBJnPDglsMoPDoL19LQtXsyCE,9303
176
+ tests/test_connectors/test_local.py,sha256=N_FkejDZKu7XLnKeApqfBARYMyxf-hRXCQJrXLHvwRg,7442
177
+ tests/test_connectors/test_ssh.py,sha256=YeCZtYDogm6M8xSFXjNXXmfgMGOH-0fzTeRaNXyOAtU,42995
178
+ tests/test_connectors/test_sshuserclient.py,sha256=Rm1zxSODDXQXuQ3qdkCA12FCUj7Zkgwwe-s2UrsZAoE,8599
179
+ tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
180
+ tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
181
+ tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
182
+ pyinfra-3.3.dist-info/LICENSE.md,sha256=BzCnRYLJv0yb-FJuEd_XOrrQSOEQKzIVo0yHT8taNnM,1076
183
+ pyinfra-3.3.dist-info/METADATA,sha256=altc2EhdZvDuH_4Nbn_T4yocK5cA7feHsKONJBvEm4E,8054
184
+ pyinfra-3.3.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
185
+ pyinfra-3.3.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
186
+ pyinfra-3.3.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
187
+ pyinfra-3.3.dist-info/RECORD,,
pyinfra_cli/exceptions.py CHANGED
@@ -5,6 +5,7 @@ from traceback import format_exception, format_tb, walk_tb
5
5
  from types import TracebackType
6
6
 
7
7
  import click
8
+ from typing_extensions import override
8
9
 
9
10
  from pyinfra import logger
10
11
  from pyinfra.api.exceptions import (
@@ -37,6 +38,7 @@ class WrappedError(click.ClickException):
37
38
  message = repr(message)
38
39
  self.message = message
39
40
 
41
+ @override
40
42
  def show(self, file=None):
41
43
  name = "unknown error"
42
44
 
@@ -65,6 +67,7 @@ class WrappedError(click.ClickException):
65
67
 
66
68
 
67
69
  class CliError(click.ClickException):
70
+ @override
68
71
  def show(self, file=None):
69
72
  logger.warning(
70
73
  "--> {0}: {1}".format(
@@ -96,6 +99,7 @@ class UnexpectedExternalError(click.ClickException, UnexpectedMixin):
96
99
  self.exception = e
97
100
  self.filename = filename
98
101
 
102
+ @override
99
103
  def show(self, file=None):
100
104
  logger.warning(
101
105
  "--> {0}:\n".format(
@@ -118,6 +122,7 @@ class UnexpectedInternalError(click.ClickException, UnexpectedMixin):
118
122
  e._traceback = traceback
119
123
  self.exception = e
120
124
 
125
+ @override
121
126
  def show(self, file=None):
122
127
  click.echo(
123
128
  "--> {0}:\n".format(
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)
pyinfra_cli/log.py CHANGED
@@ -1,12 +1,14 @@
1
1
  import logging
2
2
 
3
3
  import click
4
+ from typing_extensions import override
4
5
 
5
6
  from pyinfra import logger, state
6
7
  from pyinfra.context import ctx_state
7
8
 
8
9
 
9
10
  class LogHandler(logging.Handler):
11
+ @override
10
12
  def emit(self, record):
11
13
  try:
12
14
  message = self.format(record)
@@ -25,6 +27,7 @@ class LogFormatter(logging.Formatter):
25
27
  logging.CRITICAL: lambda s: click.style(s, "red", bold=True),
26
28
  }
27
29
 
30
+ @override
28
31
  def format(self, record):
29
32
  message = record.msg
30
33
 
pyinfra_cli/main.py CHANGED
@@ -34,7 +34,7 @@ from .util import exec_file, load_deploy_file, load_func, parse_cli_arg
34
34
  from .virtualenv import init_virtualenv
35
35
 
36
36
 
37
- def _exit():
37
+ def _exit() -> None:
38
38
  if ctx_state.isset() and state.failed_hosts:
39
39
  sys.exit(1)
40
40
  sys.exit(0)
@@ -49,7 +49,10 @@ def _print_support(ctx, param, value):
49
49
  ctx.exit()
50
50
 
51
51
 
52
- @click.command()
52
+ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
53
+
54
+
55
+ @click.command(context_settings=CONTEXT_SETTINGS)
53
56
  @click.argument("inventory", nargs=1, type=click.Path(exists=False))
54
57
  @click.argument("operations", nargs=-1, required=True, type=click.Path(exists=False))
55
58
  @click.option(
@@ -347,7 +350,6 @@ def _main(
347
350
  state.set_stage(StateStage.Connect)
348
351
  connect_all(state)
349
352
 
350
- logger.info("--> Preparing operations...")
351
353
  state.set_stage(StateStage.Prepare)
352
354
  can_diff, state, config = _handle_commands(
353
355
  state, config, command, original_operations, operations
@@ -648,6 +650,7 @@ def _apply_inventory_limit(inventory, limit):
648
650
  #
649
651
  def _handle_commands(state, config, command, original_operations, operations):
650
652
  if command is CliCommands.FACT:
653
+ logger.info("--> Gathering facts...")
651
654
  state, fact_data = _run_fact_operations(state, config, operations)
652
655
  print_facts(fact_data)
653
656
  _exit()
@@ -655,13 +658,16 @@ def _handle_commands(state, config, command, original_operations, operations):
655
658
  can_diff = True
656
659
 
657
660
  if command == CliCommands.SHELL:
661
+ logger.info("--> Preparing exec operation...")
658
662
  state = _prepare_exec_operations(state, config, operations)
659
663
  can_diff = False
660
664
 
661
665
  elif command == CliCommands.DEPLOY_FILES:
666
+ logger.info("--> Preparing operation files...")
662
667
  state, config, operations = _prepare_deploy_operations(state, config, operations)
663
668
 
664
669
  elif command == CliCommands.FUNC:
670
+ logger.info("--> Preparing operation func...")
665
671
  state, kwargs = _prepare_func_operations(
666
672
  state,
667
673
  config,
@@ -673,8 +679,6 @@ def _handle_commands(state, config, command, original_operations, operations):
673
679
 
674
680
 
675
681
  def _run_fact_operations(state, config, operations):
676
- logger.info("--> Gathering facts...")
677
-
678
682
  state.print_fact_info = True
679
683
  fact_data = {}
680
684
 
@@ -712,7 +716,6 @@ def _prepare_exec_operations(state, config, operations):
712
716
 
713
717
 
714
718
  def _prepare_deploy_operations(state, config, operations):
715
- logger.info("--> Preparing Operations...")
716
719
 
717
720
  # Number of "steps" to make = number of files * number of hosts
718
721
  for i, filename in enumerate(operations):
@@ -729,8 +732,6 @@ def _prepare_deploy_operations(state, config, operations):
729
732
 
730
733
 
731
734
  def _prepare_func_operations(state, config, operations, original_operations):
732
- logger.info("--> Preparing operation...")
733
-
734
735
  op, args = operations
735
736
  args, kwargs = args
736
737
 
pyinfra_cli/prints.py CHANGED
@@ -130,7 +130,7 @@ def print_facts(facts):
130
130
  print_fact(data)
131
131
 
132
132
 
133
- def print_support_info():
133
+ def print_support_info() -> None:
134
134
  from importlib.metadata import PackageNotFoundError, requires, version
135
135
 
136
136
  from packaging.requirements import Requirement
@@ -281,6 +281,8 @@ def print_results(state: "State"):
281
281
  (logger.info, ["Operation", "Hosts", "Success", "Error", "No Change"]),
282
282
  ]
283
283
 
284
+ totals = {"hosts": 0, "success": 0, "error": 0, "no_change": 0}
285
+
284
286
  for op_hash in state.get_op_order():
285
287
  hosts_in_op = 0
286
288
  hosts_in_op_success: list[str] = []
@@ -306,19 +308,32 @@ def print_results(state: "State"):
306
308
  str(hosts_in_op),
307
309
  ]
308
310
 
311
+ totals["hosts"] += hosts_in_op
312
+
309
313
  if hosts_in_op_success:
310
- row.append(f"{len(hosts_in_op_success)}")
314
+ num_hosts_in_op_success = len(hosts_in_op_success)
315
+ row.append(str(num_hosts_in_op_success))
316
+ totals["success"] += num_hosts_in_op_success
311
317
  else:
312
318
  row.append("-")
319
+
313
320
  if hosts_in_op_error:
314
- row.append(f"{len(hosts_in_op_error)}")
321
+ num_hosts_in_op_error = len(hosts_in_op_error)
322
+ row.append(str(num_hosts_in_op_error))
323
+ totals["error"] += num_hosts_in_op_error
315
324
  else:
316
325
  row.append("-")
326
+
317
327
  if hosts_in_op_no_change:
318
- row.append(f"{len(hosts_in_op_no_change)}")
328
+ num_hosts_in_op_no_change = len(hosts_in_op_no_change)
329
+ row.append(str(num_hosts_in_op_no_change))
330
+ totals["no_change"] += num_hosts_in_op_no_change
319
331
  else:
320
332
  row.append("-")
321
333
 
322
334
  rows.append((logger.info, row))
323
335
 
336
+ totals_row = ["Grand total"] + [str(i) if i else "-" for i in totals.values()]
337
+ rows.append((logger.info, totals_row))
338
+
324
339
  print_rows(rows)
pyinfra_cli/util.py CHANGED
@@ -124,6 +124,9 @@ def json_encode(obj):
124
124
  if isinstance(obj, bytes):
125
125
  return obj.decode()
126
126
 
127
+ if hasattr(obj, "to_json"):
128
+ return obj.to_json()
129
+
127
130
  raise TypeError("Cannot serialize: {0} ({1})".format(type(obj), obj))
128
131
 
129
132
 
pyinfra_cli/virtualenv.py CHANGED
@@ -6,7 +6,7 @@ import click
6
6
  from pyinfra import logger
7
7
 
8
8
 
9
- def init_virtualenv():
9
+ def init_virtualenv() -> None:
10
10
  """
11
11
  Add a virtualenv to sys.path so the user can import modules from it.
12
12
  This isn't perfect: it doesn't use the Python interpreter with which the
@@ -43,26 +43,28 @@ class TestCliDeployState(PatchSSHTestCase):
43
43
  assert executed is False
44
44
 
45
45
  def test_deploy(self):
46
- task_file_path = path.join("tasks", "a_task.py")
46
+ a_task_file_path = path.join("tasks", "a_task.py")
47
+ b_task_file_path = path.join("tasks", "b_task.py")
47
48
  nested_task_path = path.join("tasks", "another_task.py")
48
49
  correct_op_name_and_host_names = [
49
50
  ("First main operation", True), # true for all hosts
50
51
  ("Second main operation", ("somehost",)),
51
- ("{0} | First task operation".format(task_file_path), ("anotherhost",)),
52
- ("{0} | Task order loop 1".format(task_file_path), ("anotherhost",)),
53
- ("{0} | 2nd Task order loop 1".format(task_file_path), ("anotherhost",)),
54
- ("{0} | Task order loop 2".format(task_file_path), ("anotherhost",)),
55
- ("{0} | 2nd Task order loop 2".format(task_file_path), ("anotherhost",)),
52
+ ("{0} | First task operation".format(a_task_file_path), ("anotherhost",)),
53
+ ("{0} | Task order loop 1".format(a_task_file_path), ("anotherhost",)),
54
+ ("{0} | 2nd Task order loop 1".format(a_task_file_path), ("anotherhost",)),
55
+ ("{0} | Task order loop 2".format(a_task_file_path), ("anotherhost",)),
56
+ ("{0} | 2nd Task order loop 2".format(a_task_file_path), ("anotherhost",)),
56
57
  (
57
- "{0} | {1} | Second task operation".format(task_file_path, nested_task_path),
58
+ "{0} | {1} | Second task operation".format(a_task_file_path, nested_task_path),
58
59
  ("anotherhost",),
59
60
  ),
60
- ("{0} | First task operation".format(task_file_path), True),
61
- ("{0} | Task order loop 1".format(task_file_path), True),
62
- ("{0} | 2nd Task order loop 1".format(task_file_path), True),
63
- ("{0} | Task order loop 2".format(task_file_path), True),
64
- ("{0} | 2nd Task order loop 2".format(task_file_path), True),
65
- ("{0} | {1} | Second task operation".format(task_file_path, nested_task_path), True),
61
+ ("{0} | First task operation".format(a_task_file_path), True),
62
+ ("{0} | Task order loop 1".format(a_task_file_path), True),
63
+ ("{0} | 2nd Task order loop 1".format(a_task_file_path), True),
64
+ ("{0} | Task order loop 2".format(a_task_file_path), True),
65
+ ("{0} | 2nd Task order loop 2".format(a_task_file_path), True),
66
+ ("{0} | {1} | Second task operation".format(a_task_file_path, nested_task_path), True),
67
+ ("{0} | Important task operation".format(b_task_file_path), True),
66
68
  ("My deploy | First deploy operation", True),
67
69
  ("My deploy | My nested deploy | First nested deploy operation", True),
68
70
  ("My deploy | Second deploy operation", True),