pyinfra 2.9.2__py2.py3-none-any.whl → 3.0b1__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 (126) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +261 -255
  3. pyinfra/api/arguments_typed.py +77 -0
  4. pyinfra/api/command.py +66 -53
  5. pyinfra/api/config.py +27 -22
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +2 -24
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +77 -113
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +17 -25
  13. pyinfra/api/operation.py +232 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +55 -70
  17. pyinfra/connectors/base.py +150 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +227 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +195 -207
  22. pyinfra/connectors/ssh.py +528 -615
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +212 -137
  27. pyinfra/connectors/vagrant.py +55 -48
  28. pyinfra/context.py +3 -2
  29. pyinfra/facts/docker.py +1 -0
  30. pyinfra/facts/files.py +45 -32
  31. pyinfra/facts/git.py +3 -1
  32. pyinfra/facts/gpg.py +1 -1
  33. pyinfra/facts/hardware.py +4 -2
  34. pyinfra/facts/iptables.py +5 -3
  35. pyinfra/facts/mysql.py +1 -0
  36. pyinfra/facts/postgres.py +168 -0
  37. pyinfra/facts/postgresql.py +5 -161
  38. pyinfra/facts/selinux.py +3 -1
  39. pyinfra/facts/server.py +77 -30
  40. pyinfra/facts/systemd.py +29 -12
  41. pyinfra/facts/sysvinit.py +10 -10
  42. pyinfra/facts/util/packaging.py +4 -2
  43. pyinfra/local.py +4 -5
  44. pyinfra/operations/apk.py +3 -3
  45. pyinfra/operations/apt.py +25 -47
  46. pyinfra/operations/brew.py +7 -14
  47. pyinfra/operations/bsdinit.py +4 -4
  48. pyinfra/operations/cargo.py +1 -1
  49. pyinfra/operations/choco.py +1 -1
  50. pyinfra/operations/dnf.py +4 -4
  51. pyinfra/operations/files.py +108 -321
  52. pyinfra/operations/gem.py +1 -1
  53. pyinfra/operations/git.py +6 -37
  54. pyinfra/operations/iptables.py +2 -10
  55. pyinfra/operations/launchd.py +1 -1
  56. pyinfra/operations/lxd.py +1 -9
  57. pyinfra/operations/mysql.py +5 -28
  58. pyinfra/operations/npm.py +1 -1
  59. pyinfra/operations/openrc.py +1 -1
  60. pyinfra/operations/pacman.py +3 -3
  61. pyinfra/operations/pip.py +14 -15
  62. pyinfra/operations/pkg.py +1 -1
  63. pyinfra/operations/pkgin.py +3 -3
  64. pyinfra/operations/postgres.py +347 -0
  65. pyinfra/operations/postgresql.py +17 -380
  66. pyinfra/operations/python.py +2 -17
  67. pyinfra/operations/selinux.py +5 -28
  68. pyinfra/operations/server.py +59 -84
  69. pyinfra/operations/snap.py +1 -3
  70. pyinfra/operations/ssh.py +8 -23
  71. pyinfra/operations/systemd.py +7 -7
  72. pyinfra/operations/sysvinit.py +3 -12
  73. pyinfra/operations/upstart.py +4 -4
  74. pyinfra/operations/util/__init__.py +12 -0
  75. pyinfra/operations/util/files.py +2 -2
  76. pyinfra/operations/util/packaging.py +6 -24
  77. pyinfra/operations/util/service.py +18 -37
  78. pyinfra/operations/vzctl.py +2 -2
  79. pyinfra/operations/xbps.py +3 -3
  80. pyinfra/operations/yum.py +4 -4
  81. pyinfra/operations/zypper.py +4 -4
  82. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
  83. pyinfra-3.0b1.dist-info/RECORD +163 -0
  84. pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
  85. pyinfra_cli/__main__.py +2 -0
  86. pyinfra_cli/commands.py +7 -2
  87. pyinfra_cli/exceptions.py +83 -42
  88. pyinfra_cli/inventory.py +19 -4
  89. pyinfra_cli/log.py +17 -3
  90. pyinfra_cli/main.py +133 -90
  91. pyinfra_cli/prints.py +93 -129
  92. pyinfra_cli/util.py +60 -29
  93. tests/test_api/test_api.py +2 -0
  94. tests/test_api/test_api_arguments.py +13 -13
  95. tests/test_api/test_api_deploys.py +28 -29
  96. tests/test_api/test_api_facts.py +60 -98
  97. tests/test_api/test_api_operations.py +100 -200
  98. tests/test_cli/test_cli.py +18 -49
  99. tests/test_cli/test_cli_deploy.py +11 -37
  100. tests/test_cli/test_cli_exceptions.py +50 -19
  101. tests/test_cli/util.py +1 -1
  102. tests/test_connectors/test_chroot.py +6 -6
  103. tests/test_connectors/test_docker.py +4 -4
  104. tests/test_connectors/test_dockerssh.py +38 -50
  105. tests/test_connectors/test_local.py +11 -12
  106. tests/test_connectors/test_ssh.py +66 -107
  107. tests/test_connectors/test_terraform.py +9 -15
  108. tests/test_connectors/test_util.py +24 -46
  109. tests/test_connectors/test_vagrant.py +4 -4
  110. pyinfra/api/operation.pyi +0 -117
  111. pyinfra/connectors/ansible.py +0 -171
  112. pyinfra/connectors/mech.py +0 -186
  113. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  114. pyinfra/connectors/winrm.py +0 -320
  115. pyinfra/facts/windows.py +0 -366
  116. pyinfra/facts/windows_files.py +0 -90
  117. pyinfra/operations/windows.py +0 -59
  118. pyinfra/operations/windows_files.py +0 -551
  119. pyinfra-2.9.2.dist-info/RECORD +0 -170
  120. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  121. tests/test_connectors/test_ansible.py +0 -64
  122. tests/test_connectors/test_mech.py +0 -126
  123. tests/test_connectors/test_winrm.py +0 -76
  124. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
  125. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
  126. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/facts/files.py CHANGED
@@ -5,7 +5,9 @@ The files facts provide information about the filesystem and it's contents on th
5
5
  import re
6
6
  import stat
7
7
  from datetime import datetime
8
- from typing import List, Tuple
8
+ from typing import TYPE_CHECKING, List, Optional, Tuple, Union
9
+
10
+ from typing_extensions import Literal, NotRequired, TypedDict
9
11
 
10
12
  from pyinfra.api.command import QuoteString, make_formatted_string_command
11
13
  from pyinfra.api.facts import FactBase
@@ -64,7 +66,25 @@ def _parse_mode(mode: str) -> int:
64
66
  return int(oct(out)[2:])
65
67
 
66
68
 
67
- class File(FactBase):
69
+ def _parse_datetime(value: str) -> Optional[datetime]:
70
+ value = try_int(value)
71
+ if isinstance(value, int):
72
+ return datetime.utcfromtimestamp(value)
73
+ return None
74
+
75
+
76
+ class FileDict(TypedDict):
77
+ mode: int
78
+ size: Union[int, str]
79
+ atime: Optional[datetime]
80
+ mtime: Optional[datetime]
81
+ ctime: Optional[datetime]
82
+ user: str
83
+ group: str
84
+ link_target: NotRequired[str]
85
+
86
+
87
+ class File(FactBase[Union[FileDict, Literal[False], None]]):
68
88
  """
69
89
  Returns information about a file on the remote system:
70
90
 
@@ -98,36 +118,23 @@ class File(FactBase):
98
118
  bsd_stat_command=BSD_STAT_COMMAND,
99
119
  )
100
120
 
101
- def process(self, output):
121
+ def process(self, output) -> Union[FileDict, Literal[False], None]:
102
122
  match = re.match(STAT_REGEX, output[0])
103
123
  if not match:
104
124
  return None
105
125
 
106
- data = {}
107
- path_type = None
108
-
109
- for key, value in (
110
- ("user", match.group(1)),
111
- ("group", match.group(2)),
112
- ("mode", match.group(3)),
113
- ("atime", match.group(4)),
114
- ("mtime", match.group(5)),
115
- ("ctime", match.group(6)),
116
- ("size", match.group(7)),
117
- ):
118
- if key == "mode":
119
- path_type = FLAG_TO_TYPE[value[0]]
120
- value = _parse_mode(value[1:])
121
-
122
- elif key == "size":
123
- value = try_int(value)
124
-
125
- elif key in ("atime", "mtime", "ctime"):
126
- value = try_int(value)
127
- if isinstance(value, int):
128
- value = datetime.utcfromtimestamp(value)
129
-
130
- data[key] = value
126
+ mode = match.group(3)
127
+ path_type = FLAG_TO_TYPE[mode[0]]
128
+
129
+ data: FileDict = {
130
+ "user": match.group(1),
131
+ "group": match.group(2),
132
+ "mode": _parse_mode(mode[1:]),
133
+ "atime": _parse_datetime(match.group(4)),
134
+ "mtime": _parse_datetime(match.group(5)),
135
+ "ctime": _parse_datetime(match.group(6)),
136
+ "size": try_int(match.group(7)),
137
+ }
131
138
 
132
139
  if path_type != self.type:
133
140
  return False
@@ -205,7 +212,13 @@ class Socket(File):
205
212
  type = "socket"
206
213
 
207
214
 
208
- class HashFileFactBase(FactBase):
215
+ if TYPE_CHECKING:
216
+ FactBaseOptionalStr = FactBase[Optional[str]]
217
+ else:
218
+ FactBaseOptionalStr = FactBase
219
+
220
+
221
+ class HashFileFactBase(FactBaseOptionalStr):
209
222
  _raw_cmd: str
210
223
  _regexes: Tuple[str, str]
211
224
 
@@ -229,13 +242,14 @@ class HashFileFactBase(FactBase):
229
242
  self.path = path
230
243
  return make_formatted_string_command(self._raw_cmd, QuoteString(path))
231
244
 
232
- def process(self, output):
245
+ def process(self, output) -> Optional[str]:
233
246
  output = output[0]
234
247
  escaped_path = re.escape(self.path)
235
248
  for regex in self._regexes:
236
249
  matches = re.match(regex % escaped_path, output)
237
250
  if matches:
238
251
  return matches.group(1)
252
+ return None
239
253
 
240
254
 
241
255
  class Sha1File(HashFileFactBase, digits=40, cmds=["sha1sum", "shasum", "sha1"]):
@@ -294,6 +308,7 @@ class FindInFile(FactBase):
294
308
  class FindFilesBase(FactBase):
295
309
  abstract = True
296
310
  default = list
311
+ type_flag: str
297
312
 
298
313
  @staticmethod
299
314
  def process(output):
@@ -345,7 +360,6 @@ class Flags(FactBase):
345
360
  )
346
361
 
347
362
  def process(self, output):
348
-
349
363
  return [flag for flag in output[0].split(",") if len(flag) > 0] if len(output) == 1 else []
350
364
 
351
365
 
@@ -381,7 +395,6 @@ class Block(FactBase):
381
395
  default = list
382
396
 
383
397
  def command(self, path, marker=None, begin=None, end=None):
384
-
385
398
  self.path = path
386
399
  start = (marker or MARKER_DEFAULT).format(mark=begin or MARKER_BEGIN_DEFAULT)
387
400
  end = (marker or MARKER_DEFAULT).format(mark=end or MARKER_END_DEFAULT)
pyinfra/facts/git.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
2
4
 
3
5
  from pyinfra.api.facts import FactBase
@@ -29,7 +31,7 @@ class GitConfig(FactBase):
29
31
 
30
32
  @staticmethod
31
33
  def process(output):
32
- items = {}
34
+ items: dict[str, list[str]] = {}
33
35
 
34
36
  for line in output:
35
37
  key, value = line.split("=", 1)
pyinfra/facts/gpg.py CHANGED
@@ -48,7 +48,7 @@ class GpgFactBase(FactBase):
48
48
 
49
49
  elif current_subkey or current_key:
50
50
  target = current_subkey or current_key
51
-
51
+ assert target is not None
52
52
  if bits[0] == "fpr":
53
53
  target["fingerprint"] = bits[9] # fingerprint = field 10
54
54
  elif bits[0] == "uid":
pyinfra/facts/hardware.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
2
4
 
3
5
  from pyinfra.api import FactBase, ShortFactBase
@@ -256,7 +258,7 @@ class NetworkDevices(FactBase):
256
258
  )
257
259
  device_info["ipv4"] = ipv4_info[0]
258
260
  if len(ipv4_matches) > 1:
259
- device_info["ipv4"]["additional_ips"] = ipv4_info[1:]
261
+ device_info["ipv4"]["additional_ips"] = ipv4_info[1:] # type: ignore[index]
260
262
 
261
263
  # IPv6 Addresses
262
264
  ipv6_re = (
@@ -277,7 +279,7 @@ class NetworkDevices(FactBase):
277
279
  ipv6_info.append({"address": address, "mask_bits": int(mask_bits)})
278
280
  device_info["ipv6"] = ipv6_info[0]
279
281
  if len(ipv6_matches) > 1:
280
- device_info["ipv6"]["additional_ips"] = ipv6_info[1:]
282
+ device_info["ipv6"]["additional_ips"] = ipv6_info[1:] # type: ignore[index]
281
283
 
282
284
  all_devices[device_name] = device_info
283
285
 
pyinfra/facts/iptables.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from pyinfra.api import FactBase
2
4
 
3
5
  # Mapping for iptables code arguments to variable names
@@ -27,16 +29,16 @@ def parse_iptables_rule(line):
27
29
 
28
30
  bits = line.split()
29
31
 
30
- definition = {}
32
+ definition: dict = {}
31
33
 
32
34
  key = None
33
- args = []
35
+ args: list[str] = []
34
36
  not_arg = False
35
37
 
36
38
  def add_args():
37
39
  arg_string = " ".join(args)
38
40
 
39
- if key in IPTABLES_ARGS:
41
+ if key and key in IPTABLES_ARGS:
40
42
  definition_key = "not_{0}".format(IPTABLES_ARGS[key]) if not_arg else IPTABLES_ARGS[key]
41
43
  definition[definition_key] = arg_string
42
44
  else:
pyinfra/facts/mysql.py CHANGED
@@ -53,6 +53,7 @@ def make_execute_mysql_command(command, ignore_errors=False, **mysql_kwargs):
53
53
  class MysqlFactBase(FactBase):
54
54
  abstract = True
55
55
 
56
+ mysql_command: str
56
57
  requires_command = "mysql"
57
58
  ignore_errors = False
58
59
 
@@ -0,0 +1,168 @@
1
+ from __future__ import annotations
2
+
3
+ from pyinfra.api import FactBase, MaskString, QuoteString, StringCommand
4
+ from pyinfra.api.util import try_int
5
+
6
+ from .util.databases import parse_columns_and_rows
7
+
8
+
9
+ def make_psql_command(
10
+ database=None,
11
+ user=None,
12
+ password=None,
13
+ host=None,
14
+ port=None,
15
+ executable="psql",
16
+ ):
17
+ target_bits: list[str] = []
18
+
19
+ if password:
20
+ target_bits.append(MaskString('PGPASSWORD="{0}"'.format(password)))
21
+
22
+ target_bits.append(executable)
23
+
24
+ if database:
25
+ target_bits.append("-d {0}".format(database))
26
+
27
+ if user:
28
+ target_bits.append("-U {0}".format(user))
29
+
30
+ if host:
31
+ target_bits.append("-h {0}".format(host))
32
+
33
+ if port:
34
+ target_bits.append("-p {0}".format(port))
35
+
36
+ return StringCommand(*target_bits)
37
+
38
+
39
+ def make_execute_psql_command(command, **psql_kwargs):
40
+ return StringCommand(
41
+ make_psql_command(**psql_kwargs),
42
+ "-Ac",
43
+ QuoteString(command), # quote this whole item as a single shell argument
44
+ )
45
+
46
+
47
+ class PostgresFactBase(FactBase):
48
+ abstract = True
49
+
50
+ psql_command: str
51
+ requires_command = "psql"
52
+
53
+ def command(
54
+ self,
55
+ psql_user=None,
56
+ psql_password=None,
57
+ psql_host=None,
58
+ psql_port=None,
59
+ ):
60
+ return make_execute_psql_command(
61
+ self.psql_command,
62
+ user=psql_user,
63
+ password=psql_password,
64
+ host=psql_host,
65
+ port=psql_port,
66
+ )
67
+
68
+
69
+ class PostgresRoles(PostgresFactBase):
70
+ """
71
+ Returns a dict of PostgreSQL roles and data:
72
+
73
+ .. code:: python
74
+
75
+ {
76
+ "pyinfra": {
77
+ "super": true,
78
+ "createrole": false,
79
+ "createdb": false,
80
+ ...
81
+ },
82
+ }
83
+ """
84
+
85
+ default = dict
86
+ psql_command = "SELECT * FROM pg_catalog.pg_roles"
87
+
88
+ def process(self, output):
89
+ # Remove the last line of the output (row count)
90
+ output = output[:-1]
91
+ rows = parse_columns_and_rows(
92
+ output,
93
+ "|",
94
+ # Remove the "rol" prefix on column names
95
+ remove_column_prefix="rol",
96
+ )
97
+
98
+ users = {}
99
+
100
+ for details in rows:
101
+ for key, value in list(details.items()):
102
+ if key in ("oid", "connlimit"):
103
+ details[key] = try_int(value)
104
+
105
+ if key in (
106
+ "super",
107
+ "inherit",
108
+ "createrole",
109
+ "createdb",
110
+ "canlogin",
111
+ "replication",
112
+ "bypassrls",
113
+ ):
114
+ details[key] = value == "t"
115
+
116
+ users[details.pop("name")] = details
117
+
118
+ return users
119
+
120
+
121
+ class PostgresDatabases(PostgresFactBase):
122
+ """
123
+ Returns a dict of PostgreSQL databases and metadata:
124
+
125
+ .. code:: python
126
+
127
+ {
128
+ "pyinfra_stuff": {
129
+ "encoding": "UTF8",
130
+ "collate": "en_US.UTF-8",
131
+ "ctype": "en_US.UTF-8",
132
+ ...
133
+ },
134
+ }
135
+ """
136
+
137
+ default = dict
138
+ psql_command = "SELECT pg_catalog.pg_encoding_to_char(encoding), * FROM pg_catalog.pg_database"
139
+
140
+ def process(self, output):
141
+ # Remove the last line of the output (row count)
142
+ output = output[:-1]
143
+ rows = parse_columns_and_rows(
144
+ output,
145
+ "|",
146
+ # Remove the "dat" prefix on column names
147
+ remove_column_prefix="dat",
148
+ )
149
+
150
+ databases = {}
151
+
152
+ for details in rows:
153
+ details["encoding"] = details.pop("pg_encoding_to_char")
154
+
155
+ for key, value in list(details.items()):
156
+ if key.endswith("id") or key in (
157
+ "dba",
158
+ "tablespace",
159
+ "connlimit",
160
+ ):
161
+ details[key] = try_int(value)
162
+
163
+ if key in ("istemplate", "allowconn"):
164
+ details[key] = value == "t"
165
+
166
+ databases[details.pop("name")] = details
167
+
168
+ return databases
@@ -1,165 +1,9 @@
1
- from pyinfra.api import FactBase, MaskString, QuoteString, StringCommand
2
- from pyinfra.api.util import try_int
1
+ from .postgres import PostgresDatabases, PostgresRoles
3
2
 
4
- from .util.databases import parse_columns_and_rows
5
3
 
4
+ class PostgresqlRoles(PostgresRoles):
5
+ deprecated = True
6
6
 
7
- def make_psql_command(
8
- database=None,
9
- user=None,
10
- password=None,
11
- host=None,
12
- port=None,
13
- executable="psql",
14
- ):
15
- target_bits = []
16
7
 
17
- if password:
18
- target_bits.append(MaskString('PGPASSWORD="{0}"'.format(password)))
19
-
20
- target_bits.append(executable)
21
-
22
- if database:
23
- target_bits.append("-d {0}".format(database))
24
-
25
- if user:
26
- target_bits.append("-U {0}".format(user))
27
-
28
- if host:
29
- target_bits.append("-h {0}".format(host))
30
-
31
- if port:
32
- target_bits.append("-p {0}".format(port))
33
-
34
- return StringCommand(*target_bits)
35
-
36
-
37
- def make_execute_psql_command(command, **psql_kwargs):
38
- return StringCommand(
39
- make_psql_command(**psql_kwargs),
40
- "-Ac",
41
- QuoteString(command), # quote this whole item as a single shell argument
42
- )
43
-
44
-
45
- class PostgresqlFactBase(FactBase):
46
- abstract = True
47
-
48
- requires_command = "psql"
49
-
50
- def command(
51
- self,
52
- psql_user=None,
53
- psql_password=None,
54
- psql_host=None,
55
- psql_port=None,
56
- ):
57
- return make_execute_psql_command(
58
- self.psql_command,
59
- user=psql_user,
60
- password=psql_password,
61
- host=psql_host,
62
- port=psql_port,
63
- )
64
-
65
-
66
- class PostgresqlRoles(PostgresqlFactBase):
67
- """
68
- Returns a dict of PostgreSQL roles and data:
69
-
70
- .. code:: python
71
-
72
- {
73
- "pyinfra": {
74
- "super": true,
75
- "createrole": false,
76
- "createdb": false,
77
- ...
78
- },
79
- }
80
- """
81
-
82
- default = dict
83
- psql_command = "SELECT * FROM pg_catalog.pg_roles"
84
-
85
- def process(self, output):
86
- # Remove the last line of the output (row count)
87
- output = output[:-1]
88
- rows = parse_columns_and_rows(
89
- output,
90
- "|",
91
- # Remove the "rol" prefix on column names
92
- remove_column_prefix="rol",
93
- )
94
-
95
- users = {}
96
-
97
- for details in rows:
98
- for key, value in list(details.items()):
99
- if key in ("oid", "connlimit"):
100
- details[key] = try_int(value)
101
-
102
- if key in (
103
- "super",
104
- "inherit",
105
- "createrole",
106
- "createdb",
107
- "canlogin",
108
- "replication",
109
- "bypassrls",
110
- ):
111
- details[key] = value == "t"
112
-
113
- users[details.pop("name")] = details
114
-
115
- return users
116
-
117
-
118
- class PostgresqlDatabases(PostgresqlFactBase):
119
- """
120
- Returns a dict of PostgreSQL databases and metadata:
121
-
122
- .. code:: python
123
-
124
- {
125
- "pyinfra_stuff": {
126
- "encoding": "UTF8",
127
- "collate": "en_US.UTF-8",
128
- "ctype": "en_US.UTF-8",
129
- ...
130
- },
131
- }
132
- """
133
-
134
- default = dict
135
- psql_command = "SELECT pg_catalog.pg_encoding_to_char(encoding), * FROM pg_catalog.pg_database"
136
-
137
- def process(self, output):
138
- # Remove the last line of the output (row count)
139
- output = output[:-1]
140
- rows = parse_columns_and_rows(
141
- output,
142
- "|",
143
- # Remove the "dat" prefix on column names
144
- remove_column_prefix="dat",
145
- )
146
-
147
- databases = {}
148
-
149
- for details in rows:
150
- details["encoding"] = details.pop("pg_encoding_to_char")
151
-
152
- for key, value in list(details.items()):
153
- if key.endswith("id") or key in (
154
- "dba",
155
- "tablespace",
156
- "connlimit",
157
- ):
158
- details[key] = try_int(value)
159
-
160
- if key in ("istemplate", "allowconn"):
161
- details[key] = value == "t"
162
-
163
- databases[details.pop("name")] = details
164
-
165
- return databases
8
+ class PostgresqlDatabases(PostgresDatabases):
9
+ deprecated = True
pyinfra/facts/selinux.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
2
4
  from collections import defaultdict
3
5
 
@@ -94,7 +96,7 @@ class SEPorts(FactBase):
94
96
  return "semanage port -ln"
95
97
 
96
98
  def process(self, output):
97
- labels = defaultdict(dict)
99
+ labels: dict[str, dict] = defaultdict(dict)
98
100
  for line in output:
99
101
  m = SEPorts._regex.match(line)
100
102
  if m is None: # something went wrong