pyinfra 0.11.dev3__py3-none-any.whl → 3.5.1__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 (203) hide show
  1. pyinfra/__init__.py +9 -12
  2. pyinfra/__main__.py +4 -0
  3. pyinfra/api/__init__.py +18 -3
  4. pyinfra/api/arguments.py +406 -0
  5. pyinfra/api/arguments_typed.py +79 -0
  6. pyinfra/api/command.py +274 -0
  7. pyinfra/api/config.py +222 -28
  8. pyinfra/api/connect.py +33 -13
  9. pyinfra/api/connectors.py +27 -0
  10. pyinfra/api/deploy.py +65 -66
  11. pyinfra/api/exceptions.py +67 -18
  12. pyinfra/api/facts.py +253 -202
  13. pyinfra/api/host.py +413 -50
  14. pyinfra/api/inventory.py +121 -160
  15. pyinfra/api/operation.py +432 -262
  16. pyinfra/api/operations.py +273 -260
  17. pyinfra/api/state.py +302 -248
  18. pyinfra/api/util.py +291 -368
  19. pyinfra/connectors/base.py +173 -0
  20. pyinfra/connectors/chroot.py +212 -0
  21. pyinfra/connectors/docker.py +381 -0
  22. pyinfra/connectors/dockerssh.py +297 -0
  23. pyinfra/connectors/local.py +238 -0
  24. pyinfra/connectors/scp/__init__.py +1 -0
  25. pyinfra/connectors/scp/client.py +204 -0
  26. pyinfra/connectors/ssh.py +670 -0
  27. pyinfra/connectors/ssh_util.py +114 -0
  28. pyinfra/connectors/sshuserclient/client.py +309 -0
  29. pyinfra/connectors/sshuserclient/config.py +102 -0
  30. pyinfra/connectors/terraform.py +135 -0
  31. pyinfra/connectors/util.py +410 -0
  32. pyinfra/connectors/vagrant.py +183 -0
  33. pyinfra/context.py +145 -0
  34. pyinfra/facts/__init__.py +7 -6
  35. pyinfra/facts/apk.py +22 -7
  36. pyinfra/facts/apt.py +117 -60
  37. pyinfra/facts/brew.py +100 -15
  38. pyinfra/facts/bsdinit.py +23 -0
  39. pyinfra/facts/cargo.py +37 -0
  40. pyinfra/facts/choco.py +47 -0
  41. pyinfra/facts/crontab.py +195 -0
  42. pyinfra/facts/deb.py +94 -0
  43. pyinfra/facts/dnf.py +48 -0
  44. pyinfra/facts/docker.py +96 -23
  45. pyinfra/facts/efibootmgr.py +113 -0
  46. pyinfra/facts/files.py +630 -58
  47. pyinfra/facts/flatpak.py +77 -0
  48. pyinfra/facts/freebsd.py +70 -0
  49. pyinfra/facts/gem.py +19 -6
  50. pyinfra/facts/git.py +59 -14
  51. pyinfra/facts/gpg.py +150 -0
  52. pyinfra/facts/hardware.py +313 -167
  53. pyinfra/facts/iptables.py +72 -62
  54. pyinfra/facts/launchd.py +44 -0
  55. pyinfra/facts/lxd.py +17 -4
  56. pyinfra/facts/mysql.py +122 -86
  57. pyinfra/facts/npm.py +17 -9
  58. pyinfra/facts/openrc.py +71 -0
  59. pyinfra/facts/opkg.py +246 -0
  60. pyinfra/facts/pacman.py +50 -7
  61. pyinfra/facts/pip.py +24 -7
  62. pyinfra/facts/pipx.py +82 -0
  63. pyinfra/facts/pkg.py +15 -6
  64. pyinfra/facts/pkgin.py +35 -0
  65. pyinfra/facts/podman.py +54 -0
  66. pyinfra/facts/postgres.py +178 -0
  67. pyinfra/facts/postgresql.py +6 -147
  68. pyinfra/facts/rpm.py +105 -0
  69. pyinfra/facts/runit.py +77 -0
  70. pyinfra/facts/selinux.py +161 -0
  71. pyinfra/facts/server.py +746 -285
  72. pyinfra/facts/snap.py +88 -0
  73. pyinfra/facts/systemd.py +139 -0
  74. pyinfra/facts/sysvinit.py +59 -0
  75. pyinfra/facts/upstart.py +35 -0
  76. pyinfra/facts/util/__init__.py +17 -0
  77. pyinfra/facts/util/databases.py +4 -6
  78. pyinfra/facts/util/packaging.py +37 -6
  79. pyinfra/facts/util/units.py +30 -0
  80. pyinfra/facts/util/win_files.py +99 -0
  81. pyinfra/facts/vzctl.py +20 -13
  82. pyinfra/facts/xbps.py +35 -0
  83. pyinfra/facts/yum.py +34 -40
  84. pyinfra/facts/zfs.py +77 -0
  85. pyinfra/facts/zypper.py +42 -0
  86. pyinfra/local.py +45 -83
  87. pyinfra/operations/__init__.py +12 -0
  88. pyinfra/operations/apk.py +98 -0
  89. pyinfra/operations/apt.py +488 -0
  90. pyinfra/operations/brew.py +231 -0
  91. pyinfra/operations/bsdinit.py +59 -0
  92. pyinfra/operations/cargo.py +45 -0
  93. pyinfra/operations/choco.py +61 -0
  94. pyinfra/operations/crontab.py +191 -0
  95. pyinfra/operations/dnf.py +210 -0
  96. pyinfra/operations/docker.py +446 -0
  97. pyinfra/operations/files.py +1939 -0
  98. pyinfra/operations/flatpak.py +94 -0
  99. pyinfra/operations/freebsd/__init__.py +12 -0
  100. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  101. pyinfra/operations/freebsd/pkg.py +219 -0
  102. pyinfra/operations/freebsd/service.py +116 -0
  103. pyinfra/operations/freebsd/sysrc.py +92 -0
  104. pyinfra/operations/gem.py +47 -0
  105. pyinfra/operations/git.py +419 -0
  106. pyinfra/operations/iptables.py +311 -0
  107. pyinfra/operations/launchd.py +45 -0
  108. pyinfra/operations/lxd.py +68 -0
  109. pyinfra/operations/mysql.py +609 -0
  110. pyinfra/operations/npm.py +57 -0
  111. pyinfra/operations/openrc.py +63 -0
  112. pyinfra/operations/opkg.py +88 -0
  113. pyinfra/operations/pacman.py +81 -0
  114. pyinfra/operations/pip.py +205 -0
  115. pyinfra/operations/pipx.py +102 -0
  116. pyinfra/operations/pkg.py +70 -0
  117. pyinfra/operations/pkgin.py +91 -0
  118. pyinfra/operations/postgres.py +436 -0
  119. pyinfra/operations/postgresql.py +30 -0
  120. pyinfra/operations/puppet.py +40 -0
  121. pyinfra/operations/python.py +72 -0
  122. pyinfra/operations/runit.py +184 -0
  123. pyinfra/operations/selinux.py +189 -0
  124. pyinfra/operations/server.py +1099 -0
  125. pyinfra/operations/snap.py +117 -0
  126. pyinfra/operations/ssh.py +216 -0
  127. pyinfra/operations/systemd.py +149 -0
  128. pyinfra/operations/sysvinit.py +141 -0
  129. pyinfra/operations/upstart.py +68 -0
  130. pyinfra/operations/util/__init__.py +12 -0
  131. pyinfra/operations/util/docker.py +251 -0
  132. pyinfra/operations/util/files.py +247 -0
  133. pyinfra/operations/util/packaging.py +336 -0
  134. pyinfra/operations/util/service.py +46 -0
  135. pyinfra/operations/vzctl.py +137 -0
  136. pyinfra/operations/xbps.py +77 -0
  137. pyinfra/operations/yum.py +210 -0
  138. pyinfra/operations/zfs.py +175 -0
  139. pyinfra/operations/zypper.py +192 -0
  140. pyinfra/progress.py +44 -32
  141. pyinfra/py.typed +0 -0
  142. pyinfra/version.py +9 -1
  143. pyinfra-3.5.1.dist-info/METADATA +141 -0
  144. pyinfra-3.5.1.dist-info/RECORD +159 -0
  145. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info}/WHEEL +1 -2
  146. pyinfra-3.5.1.dist-info/entry_points.txt +12 -0
  147. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info/licenses}/LICENSE.md +1 -1
  148. pyinfra_cli/__init__.py +1 -0
  149. pyinfra_cli/cli.py +780 -0
  150. pyinfra_cli/commands.py +66 -0
  151. pyinfra_cli/exceptions.py +155 -65
  152. pyinfra_cli/inventory.py +233 -89
  153. pyinfra_cli/log.py +39 -43
  154. pyinfra_cli/main.py +26 -495
  155. pyinfra_cli/prints.py +215 -156
  156. pyinfra_cli/util.py +172 -105
  157. pyinfra_cli/virtualenv.py +25 -20
  158. pyinfra/api/connectors/__init__.py +0 -21
  159. pyinfra/api/connectors/ansible.py +0 -99
  160. pyinfra/api/connectors/docker.py +0 -178
  161. pyinfra/api/connectors/local.py +0 -169
  162. pyinfra/api/connectors/ssh.py +0 -402
  163. pyinfra/api/connectors/sshuserclient/client.py +0 -105
  164. pyinfra/api/connectors/sshuserclient/config.py +0 -90
  165. pyinfra/api/connectors/util.py +0 -63
  166. pyinfra/api/connectors/vagrant.py +0 -155
  167. pyinfra/facts/init.py +0 -176
  168. pyinfra/facts/util/files.py +0 -102
  169. pyinfra/hook.py +0 -41
  170. pyinfra/modules/__init__.py +0 -11
  171. pyinfra/modules/apk.py +0 -64
  172. pyinfra/modules/apt.py +0 -272
  173. pyinfra/modules/brew.py +0 -122
  174. pyinfra/modules/files.py +0 -711
  175. pyinfra/modules/gem.py +0 -30
  176. pyinfra/modules/git.py +0 -115
  177. pyinfra/modules/init.py +0 -344
  178. pyinfra/modules/iptables.py +0 -271
  179. pyinfra/modules/lxd.py +0 -45
  180. pyinfra/modules/mysql.py +0 -347
  181. pyinfra/modules/npm.py +0 -47
  182. pyinfra/modules/pacman.py +0 -60
  183. pyinfra/modules/pip.py +0 -99
  184. pyinfra/modules/pkg.py +0 -43
  185. pyinfra/modules/postgresql.py +0 -245
  186. pyinfra/modules/puppet.py +0 -20
  187. pyinfra/modules/python.py +0 -37
  188. pyinfra/modules/server.py +0 -524
  189. pyinfra/modules/ssh.py +0 -150
  190. pyinfra/modules/util/files.py +0 -52
  191. pyinfra/modules/util/packaging.py +0 -118
  192. pyinfra/modules/vzctl.py +0 -133
  193. pyinfra/modules/yum.py +0 -171
  194. pyinfra/pseudo_modules.py +0 -64
  195. pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
  196. pyinfra-0.11.dev3.dist-info/METADATA +0 -135
  197. pyinfra-0.11.dev3.dist-info/RECORD +0 -95
  198. pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
  199. pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
  200. pyinfra_cli/__main__.py +0 -40
  201. pyinfra_cli/config.py +0 -92
  202. /pyinfra/{modules/util → connectors}/__init__.py +0 -0
  203. /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from typing_extensions import override
6
+
7
+ from pyinfra.api import FactBase
8
+
9
+
10
+ class OpenrcStatus(FactBase):
11
+ """
12
+ Returns a dict of name -> status for OpenRC services for a given runlevel.
13
+ """
14
+
15
+ default = dict
16
+ regex = (
17
+ r"\s+([a-zA-Z0-9\-_]+)"
18
+ r"\s+\[\s+"
19
+ r"([a-z]+)"
20
+ r"(?:\s(?:[0-9]+\sday\(s\)\s)?"
21
+ r"[0-9]+\:[0-9]+\:[0-9]+\s\([0-9]+\))?"
22
+ r"\s+\]"
23
+ )
24
+
25
+ @override
26
+ def requires_command(self, runlevel="default") -> str:
27
+ return "rc-status"
28
+
29
+ @override
30
+ def command(self, runlevel="default"):
31
+ return "rc-status {0}".format(runlevel)
32
+
33
+ @override
34
+ def process(self, output):
35
+ services = {}
36
+
37
+ for line in output:
38
+ matches = re.match(self.regex, line)
39
+ if matches:
40
+ services[matches.group(1)] = matches.group(2) == "started"
41
+
42
+ return services
43
+
44
+
45
+ class OpenrcEnabled(FactBase):
46
+ """
47
+ Returns a dict of name -> whether enabled for OpenRC services for a given runlevel.
48
+ """
49
+
50
+ default = dict
51
+
52
+ @override
53
+ def requires_command(self, runlevel="default") -> str:
54
+ return "rc-update"
55
+
56
+ @override
57
+ def command(self, runlevel="default"):
58
+ self.runlevel = runlevel
59
+ return "rc-update show -v"
60
+
61
+ @override
62
+ def process(self, output):
63
+ services = {}
64
+
65
+ for line in output:
66
+ name, levels = line.split("|", 1)
67
+ name = name.strip()
68
+ levels = levels.split()
69
+ services[name] = self.runlevel in levels
70
+
71
+ return services
pyinfra/facts/opkg.py ADDED
@@ -0,0 +1,246 @@
1
+ """
2
+ Gather the information provided by ``opkg`` on OpenWrt systems:
3
+ + ``opkg`` configuration
4
+ + feeds configuration
5
+ + list of installed packages
6
+ + list of packages with available upgrades
7
+
8
+
9
+ see https://openwrt.org/docs/guide-user/additional-software/opkg
10
+ """
11
+
12
+ import re
13
+ from typing import Dict, NamedTuple, Union
14
+
15
+ from typing_extensions import override
16
+
17
+ from pyinfra import logger
18
+ from pyinfra.api import FactBase
19
+ from pyinfra.facts.util.packaging import parse_packages
20
+
21
+ # TODO - change NamedTuple to dataclass Opkgbut need to figure out how to get json serialization
22
+ # to work without changing core code
23
+
24
+
25
+ class OpkgPkgUpgradeInfo(NamedTuple):
26
+ installed: str
27
+ available: str
28
+
29
+
30
+ class OpkgConfInfo(NamedTuple):
31
+ paths: Dict[str, str] # list of paths, e.g. {'root':'/', 'ram':'/tmp}
32
+ list_dir: str # where package lists are stored, e.g. /var/opkg-lists
33
+ options: Dict[
34
+ str, Union[str, bool]
35
+ ] # mapping from option to value, e.g. {'check_signature': True}
36
+ arch_cfg: Dict[str, int] # priorities for architectures
37
+
38
+
39
+ class OpkgFeedInfo(NamedTuple):
40
+ url: str # url for the feed
41
+ fmt: str # format of the feed, e.g. "src/gz"
42
+ kind: str # whether it comes from the 'distribution' or is 'custom'
43
+
44
+
45
+ class OpkgConf(FactBase):
46
+ """
47
+ Returns a NamedTuple with the current configuration:
48
+
49
+ .. code:: python
50
+
51
+ ConfInfo(
52
+ paths = {
53
+ "root": "/",
54
+ "ram": "/tmp",
55
+ },
56
+ list_dir = "/opt/opkg-lists",
57
+ options = {
58
+ "overlay_root": "/overlay"
59
+ },
60
+ arch_cfg = {
61
+ "all": 1,
62
+ "noarch": 1,
63
+ "i386_pentium": 10
64
+ }
65
+ )
66
+
67
+ """
68
+
69
+ regex = re.compile(
70
+ r"""
71
+ ^(?:\s*)
72
+ (?:
73
+ (?:arch\s+(?P<arch>\w+)\s+(?P<priority>\d+))|
74
+ (?:dest\s+(?P<dest>\w+)\s+(?P<dest_path>[\w/\-]+))|
75
+ (?:lists_dir\s+(?P<lists_dir>ext)\s+(?P<list_path>[\w/\-]+))|
76
+ (?:option\s+(?P<option>\w+)(?:\s+(?P<value>[^#]+))?)
77
+ )?
78
+ (?:\s*\#.*)?
79
+ $
80
+ """,
81
+ re.X,
82
+ )
83
+
84
+ @override
85
+ @staticmethod
86
+ def default():
87
+ return OpkgConfInfo({}, "", {}, {})
88
+
89
+ @override
90
+ def command(self) -> str:
91
+ return "cat /etc/opkg.conf"
92
+
93
+ @override
94
+ def process(self, output):
95
+ dest, lists_dir, options, arch_cfg = {}, "", {}, {}
96
+ for line in output:
97
+ match = self.regex.match(line)
98
+
99
+ if match is None:
100
+ logger.warning(f"Opkg: could not parse opkg.conf line '{line}'")
101
+ elif match.group("arch") is not None:
102
+ arch_cfg[match.group("arch")] = int(match.group("priority"))
103
+ elif match.group("dest") is not None:
104
+ dest[match.group("dest")] = match.group("dest_path")
105
+ elif match.group("lists_dir") is not None:
106
+ lists_dir = match.group("list_path")
107
+ elif match.group("option") is not None:
108
+ options[match.group("option")] = match.group("value") or True
109
+
110
+ return OpkgConfInfo(dest, lists_dir, options, arch_cfg)
111
+
112
+
113
+ class OpkgFeeds(FactBase):
114
+ """
115
+ Returns a dictionary containing the information for the distribution-provided and
116
+ custom opkg feeds:
117
+
118
+ .. code:: python
119
+
120
+ {
121
+ 'openwrt_base': FeedInfo(url='http://downloads ... /i386_pentium/base', fmt='src/gz', kind='distribution'), # noqa: E501
122
+ 'openwrt_core': FeedInfo(url='http://downloads ... /x86/geode/packages', fmt='src/gz', kind='distribution'), # noqa: E501
123
+ 'openwrt_luci': FeedInfo(url='http://downloads ... /i386_pentium/luci', fmt='src/gz', kind='distribution'),# noqa: E501
124
+ 'openwrt_packages': FeedInfo(url='http://downloads ... /i386_pentium/packages', fmt='src/gz', kind='distribution'),# noqa: E501
125
+ 'openwrt_routing': FeedInfo(url='http://downloads ... /i386_pentium/routing', fmt='src/gz', kind='distribution'),# noqa: E501
126
+ 'openwrt_telephony': FeedInfo(url='http://downloads ... /i386_pentium/telephony', fmt='src/gz', kind='distribution') # noqa: E501
127
+ }
128
+ """
129
+
130
+ regex = re.compile(
131
+ r"^(CUSTOM)|(?:\s*(?P<fmt>[\w/]+)\s+(?P<name>[\w]+)\s+(?P<url>[\w./:]+))?(?:\s*#.*)?$"
132
+ )
133
+ default = dict
134
+
135
+ @override
136
+ def command(self) -> str:
137
+ return "cat /etc/opkg/distfeeds.conf; echo CUSTOM; cat /etc/opkg/customfeeds.conf"
138
+
139
+ @override
140
+ def process(self, output):
141
+ feeds, kind = {}, "distribution"
142
+ for line in output:
143
+ match = self.regex.match(line)
144
+
145
+ if match is None:
146
+ logger.warning(f"Opkg: could not parse /etc/opkg/*feeds.conf line '{line}'")
147
+ elif match.group(0) == "CUSTOM":
148
+ kind = "custom"
149
+ elif match.group("name") is not None:
150
+ feeds[match.group("name")] = OpkgFeedInfo(
151
+ match.group("url"), match.group("fmt"), kind
152
+ )
153
+
154
+ return feeds
155
+
156
+
157
+ class OpkgInstallableArchitectures(FactBase):
158
+ """
159
+ Returns a dictionary containing the currently installable architectures for this system along
160
+ with their priority:
161
+
162
+ .. code:: python
163
+
164
+ {
165
+ 'all': 1,
166
+ 'i386_pentium': 10,
167
+ 'noarch': 1
168
+ }
169
+ """
170
+
171
+ regex = re.compile(r"^(?:\s*arch\s+(?P<arch>[\w]+)\s+(?P<prio>\d+))?(\s*#.*)?$")
172
+ default = dict
173
+
174
+ @override
175
+ def command(self) -> str:
176
+ return "/bin/opkg print-architecture"
177
+
178
+ @override
179
+ def process(self, output):
180
+ arch_list = {}
181
+ for line in output:
182
+ match = self.regex.match(line)
183
+
184
+ if match is None:
185
+ logger.warning(f"could not parse arch line '{line}'")
186
+ elif match.group("arch") is not None:
187
+ arch_list[match.group("arch")] = int(match.group("prio"))
188
+
189
+ return arch_list
190
+
191
+
192
+ class OpkgPackages(FactBase):
193
+ """
194
+ Returns a dict of installed opkg packages:
195
+
196
+ .. code:: python
197
+
198
+ {
199
+ 'package_name': ['version'],
200
+ ...
201
+ }
202
+ """
203
+
204
+ regex = r"^([a-zA-Z0-9][\w\-\.]*)\s-\s([\w\-\.]+)"
205
+ default = dict
206
+
207
+ @override
208
+ def command(self) -> str:
209
+ return "/bin/opkg list-installed"
210
+
211
+ @override
212
+ def process(self, output):
213
+ return parse_packages(self.regex, sorted(output))
214
+
215
+
216
+ class OpkgUpgradeablePackages(FactBase):
217
+ """
218
+ Returns a dict of installed and upgradable opkg packages:
219
+
220
+ .. code:: python
221
+
222
+ {
223
+ 'package_name': (installed='1.2.3', available='1.2.8')
224
+ ...
225
+ }
226
+ """
227
+
228
+ regex = re.compile(r"^([a-zA-Z0-9][\w\-.]*)\s-\s([\w\-.]+)\s-\s([\w\-.]+)")
229
+ default = dict
230
+ use_default_on_error = True
231
+
232
+ @override
233
+ def command(self) -> str:
234
+ return "/bin/opkg list-upgradable" # yes, really spelled that way
235
+
236
+ @override
237
+ def process(self, output):
238
+ result = {}
239
+ for line in output:
240
+ match = self.regex.match(line)
241
+ if match and len(match.groups()) == 3:
242
+ result[match.group(1)] = OpkgPkgUpgradeInfo(match.group(2), match.group(3))
243
+ else:
244
+ logger.warning(f"Opkg: could not list-upgradable line '{line}'")
245
+
246
+ return result
pyinfra/facts/pacman.py CHANGED
@@ -1,22 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ import shlex
4
+
5
+ from typing_extensions import override
6
+
1
7
  from pyinfra.api import FactBase
2
8
 
3
9
  from .util.packaging import parse_packages
4
10
 
5
- PACMAN_REGEX = r'^([a-zA-Z\-]+)\s([0-9\._+a-z\-]+)'
11
+ PACMAN_REGEX = r"^([0-9a-zA-Z\-_]+)\s([0-9\._+a-z\-:]+)"
12
+
13
+
14
+ class PacmanUnpackGroup(FactBase):
15
+ """
16
+ Returns a list of actual packages belonging to the provided package name,
17
+ expanding groups or virtual packages.
18
+
19
+ .. code:: python
20
+
21
+ [
22
+ "package_name",
23
+ ]
24
+ """
25
+
26
+ @override
27
+ def requires_command(self, *args, **kwargs) -> str:
28
+ return "pacman"
29
+
30
+ default = list
31
+
32
+ @override
33
+ def command(self, package):
34
+ # Accept failure here (|| true) for invalid/unknown packages
35
+ return 'pacman -S --print-format "%n" {0} || true'.format(shlex.quote(package))
36
+
37
+ @override
38
+ def process(self, output):
39
+ return output
6
40
 
7
41
 
8
42
  class PacmanPackages(FactBase):
9
- '''
43
+ """
10
44
  Returns a dict of installed pacman packages:
11
45
 
12
46
  .. code:: python
13
47
 
14
- 'package_name': ['version'],
15
- ...
16
- '''
48
+ {
49
+ "package_name": ["version"],
50
+ }
51
+ """
52
+
53
+ @override
54
+ def command(self) -> str:
55
+ return "pacman -Q"
56
+
57
+ @override
58
+ def requires_command(self, *args, **kwargs) -> str:
59
+ return "pacman"
17
60
 
18
- command = 'pacman -Q'
19
- deafult = dict
61
+ default = dict
20
62
 
63
+ @override
21
64
  def process(self, output):
22
65
  return parse_packages(PACMAN_REGEX, output)
pyinfra/facts/pip.py CHANGED
@@ -1,24 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import override
4
+
1
5
  from pyinfra.api import FactBase
2
6
 
3
7
  from .util.packaging import parse_packages
4
8
 
5
- PIP_REGEX = r'^([a-zA-Z0-9_\-\+\.]+)==([0-9\.]+[a-z0-9\-]*)$'
9
+ PIP_REGEX = r"^([a-zA-Z0-9_\-\+\.]+)==([0-9\.]+[a-z0-9\-]*)$"
6
10
 
7
11
 
8
12
  class PipPackages(FactBase):
9
- '''
13
+ """
10
14
  Returns a dict of installed pip packages:
11
15
 
12
16
  .. code:: python
13
17
 
14
- 'package_name': ['version'],
15
- ...
16
- '''
18
+ {
19
+ "package_name": ["version"],
20
+ }
21
+ """
17
22
 
18
23
  default = dict
24
+ pip_command = "pip"
25
+
26
+ @override
27
+ def requires_command(self, pip=None):
28
+ return pip or self.pip_command
19
29
 
20
- def command(self, pip='pip'):
21
- return '{0} freeze --all'.format(pip)
30
+ @override
31
+ def command(self, pip=None):
32
+ pip = pip or self.pip_command
33
+ return "{0} freeze --all".format(pip)
22
34
 
35
+ @override
23
36
  def process(self, output):
24
37
  return parse_packages(PIP_REGEX, output)
38
+
39
+
40
+ class Pip3Packages(PipPackages):
41
+ pip_command = "pip3"
pyinfra/facts/pipx.py ADDED
@@ -0,0 +1,82 @@
1
+ import re
2
+
3
+ from typing_extensions import override
4
+
5
+ from pyinfra.api import FactBase
6
+
7
+ from .util.packaging import parse_packages
8
+
9
+
10
+ # TODO: move to an utils file
11
+ def parse_environment(output):
12
+ environment_REGEX = r"^(?P<key>[A-Z_]+)=(?P<value>.*)$"
13
+ environment_variables = {}
14
+
15
+ for line in output:
16
+ matches = re.match(environment_REGEX, line)
17
+
18
+ if matches:
19
+ environment_variables[matches.group("key")] = matches.group("value")
20
+
21
+ return environment_variables
22
+
23
+
24
+ PIPX_REGEX = r"^([a-zA-Z0-9_\-\+\.]+)\s+([0-9\.]+[a-z0-9\-]*)$"
25
+
26
+
27
+ class PipxPackages(FactBase):
28
+ """
29
+ Returns a dict of installed pipx packages:
30
+
31
+ .. code:: python
32
+
33
+ {
34
+ "package_name": ["version"],
35
+ }
36
+ """
37
+
38
+ default = dict
39
+
40
+ @override
41
+ def requires_command(self) -> str:
42
+ return "pipx"
43
+
44
+ @override
45
+ def command(self) -> str:
46
+ return "pipx list --short"
47
+
48
+ @override
49
+ def process(self, output):
50
+ return parse_packages(PIPX_REGEX, output)
51
+
52
+
53
+ class PipxEnvironment(FactBase):
54
+ """
55
+ Returns a dict of pipx environment variables:
56
+
57
+ .. code:: python
58
+
59
+ {
60
+ "PIPX_HOME": "/home/doodba/.local/pipx",
61
+ "PIPX_BIN_DIR": "/home/doodba/.local/bin",
62
+ "PIPX_SHARED_LIBS": "/home/doodba/.local/pipx/shared",
63
+ "PIPX_LOCAL_VENVS": "/home/doodba/.local/pipx/venvs",
64
+ "PIPX_LOG_DIR": "/home/doodba/.local/pipx/logs",
65
+ "PIPX_TRASH_DIR": "/home/doodba/.local/pipx/.trash",
66
+ "PIPX_VENV_CACHEDIR": "/home/doodba/.local/pipx/.cache",
67
+ }
68
+ """
69
+
70
+ default = dict
71
+
72
+ @override
73
+ def requires_command(self) -> str:
74
+ return "pipx"
75
+
76
+ @override
77
+ def command(self) -> str:
78
+ return "pipx environment"
79
+
80
+ @override
81
+ def process(self, output):
82
+ return parse_environment(output)
pyinfra/facts/pkg.py CHANGED
@@ -1,21 +1,30 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import override
4
+
1
5
  from pyinfra.api import FactBase
2
6
 
3
7
  from .util.packaging import parse_packages
4
8
 
5
9
 
6
10
  class PkgPackages(FactBase):
7
- '''
11
+ """
8
12
  Returns a dict of installed pkg packages:
9
13
 
10
14
  .. code:: python
11
15
 
12
- 'package_name': ['version'],
13
- ...
14
- '''
16
+ {
17
+ "package_name": ["version"],
18
+ }
19
+ """
15
20
 
16
- command = 'pkg_info'
17
- regex = r'^([a-zA-Z0-9_\-\+]+)\-([0-9a-z\.]+)'
21
+ regex = r"^([a-zA-Z0-9_\-\+]+)\-([0-9a-z\.]+)"
18
22
  default = dict
19
23
 
24
+ @override
25
+ def command(self) -> str:
26
+ return "pkg info || pkg_info || true"
27
+
28
+ @override
20
29
  def process(self, output):
21
30
  return parse_packages(self.regex, output)
pyinfra/facts/pkgin.py ADDED
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import override
4
+
5
+ from pyinfra.api import FactBase
6
+
7
+ from .util.packaging import parse_packages
8
+
9
+ PKGIN_REGEX = r"^([a-zA-Z\-0-9]+)-([0-9\.]+\-?[a-z0-9]*)\s"
10
+
11
+
12
+ class PkginPackages(FactBase):
13
+ """
14
+ Returns a dict of installed pkgin packages:
15
+
16
+ .. code:: python
17
+
18
+ {
19
+ "package_name": ["version"],
20
+ }
21
+ """
22
+
23
+ @override
24
+ def command(self) -> str:
25
+ return "pkgin list"
26
+
27
+ @override
28
+ def requires_command(self) -> str:
29
+ return "pkgin"
30
+
31
+ default = dict
32
+
33
+ @override
34
+ def process(self, output):
35
+ return parse_packages(PKGIN_REGEX, output)
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any, Dict, Iterable, List, TypeVar
5
+
6
+ from typing_extensions import override
7
+
8
+ from pyinfra.api import FactBase
9
+
10
+ T = TypeVar("T")
11
+
12
+
13
+ class PodmanFactBase(FactBase[T]):
14
+ """
15
+ Base for facts using `podman` to retrieve
16
+ """
17
+
18
+ abstract = True
19
+
20
+ @override
21
+ def requires_command(self, *args, **kwargs) -> str:
22
+ return "podman"
23
+
24
+
25
+ class PodmanSystemInfo(PodmanFactBase[Dict[str, Any]]):
26
+ """
27
+ Output of 'podman system info'
28
+ """
29
+
30
+ @override
31
+ def command(self) -> str:
32
+ return "podman system info --format=json"
33
+
34
+ @override
35
+ def process(self, output: Iterable[str]) -> Dict[str, Any]:
36
+ output = json.loads(("").join(output))
37
+ assert isinstance(output, dict)
38
+ return output
39
+
40
+
41
+ class PodmanPs(PodmanFactBase[List[Dict[str, Any]]]):
42
+ """
43
+ Output of 'podman ps'
44
+ """
45
+
46
+ @override
47
+ def command(self) -> str:
48
+ return "podman ps --format=json --all"
49
+
50
+ @override
51
+ def process(self, output: Iterable[str]) -> List[Dict[str, Any]]:
52
+ output = json.loads(("").join(output))
53
+ assert isinstance(output, list)
54
+ return output # type: ignore