pyinfra 3.1.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 (45) hide show
  1. pyinfra/api/arguments.py +9 -2
  2. pyinfra/api/deploy.py +4 -2
  3. pyinfra/api/host.py +5 -3
  4. pyinfra/connectors/docker.py +17 -6
  5. pyinfra/connectors/sshuserclient/client.py +26 -14
  6. pyinfra/facts/apk.py +3 -1
  7. pyinfra/facts/apt.py +60 -0
  8. pyinfra/facts/crontab.py +190 -0
  9. pyinfra/facts/docker.py +6 -0
  10. pyinfra/facts/efibootmgr.py +108 -0
  11. pyinfra/facts/files.py +93 -6
  12. pyinfra/facts/git.py +3 -2
  13. pyinfra/facts/mysql.py +1 -2
  14. pyinfra/facts/opkg.py +233 -0
  15. pyinfra/facts/pipx.py +74 -0
  16. pyinfra/facts/podman.py +47 -0
  17. pyinfra/facts/postgres.py +2 -0
  18. pyinfra/facts/server.py +39 -77
  19. pyinfra/facts/util/units.py +30 -0
  20. pyinfra/facts/zfs.py +22 -19
  21. pyinfra/local.py +3 -2
  22. pyinfra/operations/apt.py +27 -20
  23. pyinfra/operations/crontab.py +189 -0
  24. pyinfra/operations/docker.py +13 -12
  25. pyinfra/operations/files.py +18 -0
  26. pyinfra/operations/git.py +23 -7
  27. pyinfra/operations/opkg.py +88 -0
  28. pyinfra/operations/pip.py +3 -2
  29. pyinfra/operations/pipx.py +90 -0
  30. pyinfra/operations/postgres.py +15 -11
  31. pyinfra/operations/runit.py +2 -0
  32. pyinfra/operations/server.py +3 -177
  33. pyinfra/operations/zfs.py +3 -3
  34. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/METADATA +11 -12
  35. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/RECORD +45 -36
  36. pyinfra_cli/inventory.py +26 -9
  37. pyinfra_cli/prints.py +18 -3
  38. pyinfra_cli/util.py +3 -0
  39. tests/test_cli/test_cli_deploy.py +15 -13
  40. tests/test_cli/test_cli_inventory.py +53 -0
  41. tests/test_connectors/test_sshuserclient.py +68 -1
  42. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/LICENSE.md +0 -0
  43. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/WHEEL +0 -0
  44. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/entry_points.txt +0 -0
  45. {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/top_level.txt +0 -0
pyinfra/operations/pip.py CHANGED
@@ -174,12 +174,13 @@ def packages(
174
174
  install_command_args.append(extra_install_args)
175
175
  install_command = " ".join(install_command_args)
176
176
 
177
+ upgrade_command = "{0} --upgrade".format(install_command)
177
178
  uninstall_command = " ".join([pip, "uninstall", "--yes"])
178
179
 
179
180
  # (un)Install requirements
180
181
  if requirements is not None:
181
182
  if present:
182
- yield "{0} -r {1}".format(install_command, requirements)
183
+ yield "{0} -r {1}".format(upgrade_command if latest else install_command, requirements)
183
184
  else:
184
185
  yield "{0} -r {1}".format(uninstall_command, requirements)
185
186
 
@@ -199,7 +200,7 @@ def packages(
199
200
  present,
200
201
  install_command=install_command,
201
202
  uninstall_command=uninstall_command,
202
- upgrade_command="{0} --upgrade".format(install_command),
203
+ upgrade_command=upgrade_command,
203
204
  version_join="==",
204
205
  latest=latest,
205
206
  )
@@ -0,0 +1,90 @@
1
+ """
2
+ Manage pipx (python) applications.
3
+ """
4
+
5
+ from pyinfra import host
6
+ from pyinfra.api import operation
7
+ from pyinfra.facts.pipx import PipxEnvironment, PipxPackages
8
+ from pyinfra.facts.server import Path
9
+
10
+ from .util.packaging import ensure_packages
11
+
12
+
13
+ @operation()
14
+ def packages(
15
+ packages=None,
16
+ present=True,
17
+ latest=False,
18
+ extra_args=None,
19
+ ):
20
+ """
21
+ Install/remove/update pipx packages.
22
+
23
+ + packages: list of packages to ensure
24
+ + present: whether the packages should be installed
25
+ + latest: whether to upgrade packages without a specified version
26
+ + extra_args: additional arguments to the pipx command
27
+
28
+ Versions:
29
+ Package versions can be pinned like pip: ``<pkg>==<version>``.
30
+
31
+ **Example:**
32
+
33
+ .. code:: python
34
+
35
+ pipx.packages(
36
+ name="Install ",
37
+ packages=["pyinfra"],
38
+ )
39
+ """
40
+
41
+ prep_install_command = ["pipx", "install"]
42
+
43
+ if extra_args:
44
+ prep_install_command.append(extra_args)
45
+ install_command = " ".join(prep_install_command)
46
+
47
+ uninstall_command = "pipx uninstall"
48
+ upgrade_command = "pipx upgrade"
49
+
50
+ current_packages = host.get_fact(PipxPackages)
51
+
52
+ # pipx support only one package name at a time
53
+ for package in packages:
54
+ yield from ensure_packages(
55
+ host,
56
+ [package],
57
+ current_packages,
58
+ present,
59
+ install_command=install_command,
60
+ uninstall_command=uninstall_command,
61
+ upgrade_command=upgrade_command,
62
+ version_join="==",
63
+ latest=latest,
64
+ )
65
+
66
+
67
+ @operation()
68
+ def upgrade_all():
69
+ """
70
+ Upgrade all pipx packages.
71
+ """
72
+ yield "pipx upgrade-all"
73
+
74
+
75
+ @operation()
76
+ def ensure_path():
77
+ """
78
+ Ensure pipx bin dir is in the PATH.
79
+ """
80
+
81
+ # Fetch the current user's PATH
82
+ path = host.get_fact(Path)
83
+ # Fetch the pipx environment variables
84
+ pipx_env = host.get_fact(PipxEnvironment)
85
+
86
+ # If the pipx bin dir is already in the user's PATH, we're done
87
+ if "PIPX_BIN_DIR" in pipx_env and pipx_env["PIPX_BIN_DIR"] in path.split(":"):
88
+ host.noop("pipx bin dir is already in the PATH")
89
+ else:
90
+ yield "pipx ensurepath"
@@ -8,6 +8,7 @@ All operations in this module take four optional arguments:
8
8
  + ``psql_password``: the password for the connecting user
9
9
  + ``psql_host``: the hostname of the server to connect to
10
10
  + ``psql_port``: the port of the server to connect to
11
+ + ``psql_database``: the database on the server to connect to
11
12
 
12
13
  See example/postgresql.py for detailed example
13
14
 
@@ -28,28 +29,27 @@ from pyinfra.facts.postgres import (
28
29
  @operation(is_idempotent=False)
29
30
  def sql(
30
31
  sql: str,
31
- database: str | None = None,
32
32
  # Details for speaking to PostgreSQL via `psql` CLI
33
33
  psql_user: str | None = None,
34
34
  psql_password: str | None = None,
35
35
  psql_host: str | None = None,
36
36
  psql_port: int | None = None,
37
+ psql_database: str | None = None,
37
38
  ):
38
39
  """
39
40
  Execute arbitrary SQL against PostgreSQL.
40
41
 
41
42
  + sql: SQL command(s) to execute
42
- + database: optional database to execute against
43
43
  + psql_*: global module arguments, see above
44
44
  """
45
45
 
46
46
  yield make_execute_psql_command(
47
47
  sql,
48
- database=database,
49
48
  user=psql_user,
50
49
  password=psql_password,
51
50
  host=psql_host,
52
51
  port=psql_port,
52
+ database=psql_database,
53
53
  )
54
54
 
55
55
 
@@ -70,6 +70,7 @@ def role(
70
70
  psql_password: str | None = None,
71
71
  psql_host: str | None = None,
72
72
  psql_port: int | None = None,
73
+ psql_database: str | None = None,
73
74
  ):
74
75
  """
75
76
  Add/remove PostgreSQL roles.
@@ -112,6 +113,7 @@ def role(
112
113
  psql_password=psql_password,
113
114
  psql_host=psql_host,
114
115
  psql_port=psql_port,
116
+ psql_database=psql_database,
115
117
  )
116
118
 
117
119
  is_present = role in roles
@@ -125,6 +127,7 @@ def role(
125
127
  password=psql_password,
126
128
  host=psql_host,
127
129
  port=psql_port,
130
+ database=psql_database,
128
131
  )
129
132
  else:
130
133
  host.noop("postgresql role {0} does not exist".format(role))
@@ -157,6 +160,7 @@ def role(
157
160
  password=psql_password,
158
161
  host=psql_host,
159
162
  port=psql_port,
163
+ database=psql_database,
160
164
  )
161
165
  else:
162
166
  host.noop("postgresql role {0} exists".format(role))
@@ -178,6 +182,7 @@ def database(
178
182
  psql_password: str | None = None,
179
183
  psql_host: str | None = None,
180
184
  psql_port: int | None = None,
185
+ psql_database: str | None = None,
181
186
  ):
182
187
  """
183
188
  Add/remove PostgreSQL databases.
@@ -218,6 +223,7 @@ def database(
218
223
  psql_password=psql_password,
219
224
  psql_host=psql_host,
220
225
  psql_port=psql_port,
226
+ psql_database=psql_database,
221
227
  )
222
228
 
223
229
  is_present = database in current_databases
@@ -230,6 +236,7 @@ def database(
230
236
  password=psql_password,
231
237
  host=psql_host,
232
238
  port=psql_port,
239
+ database=psql_database,
233
240
  )
234
241
  else:
235
242
  host.noop("postgresql database {0} does not exist".format(database))
@@ -257,6 +264,7 @@ def database(
257
264
  password=psql_password,
258
265
  host=psql_host,
259
266
  port=psql_port,
267
+ database=psql_database,
260
268
  )
261
269
  else:
262
270
  host.noop("postgresql database {0} exists".format(database))
@@ -265,18 +273,17 @@ def database(
265
273
  @operation(is_idempotent=False)
266
274
  def dump(
267
275
  dest: str,
268
- database: str | None = None,
269
276
  # Details for speaking to PostgreSQL via `psql` CLI
270
277
  psql_user: str | None = None,
271
278
  psql_password: str | None = None,
272
279
  psql_host: str | None = None,
273
280
  psql_port: int | None = None,
281
+ psql_database: str | None = None,
274
282
  ):
275
283
  """
276
284
  Dump a PostgreSQL database into a ``.sql`` file. Requires ``pg_dump``.
277
285
 
278
286
  + dest: name of the file to dump the SQL to
279
- + database: name of the database to dump
280
287
  + psql_*: global module arguments, see above
281
288
 
282
289
  **Example:**
@@ -286,7 +293,6 @@ def dump(
286
293
  postgresql.dump(
287
294
  name="Dump the pyinfra_stuff database",
288
295
  dest="/tmp/pyinfra_stuff.dump",
289
- database="pyinfra_stuff",
290
296
  sudo_user="postgres",
291
297
  )
292
298
 
@@ -295,11 +301,11 @@ def dump(
295
301
  yield StringCommand(
296
302
  make_psql_command(
297
303
  executable="pg_dump",
298
- database=database,
299
304
  user=psql_user,
300
305
  password=psql_password,
301
306
  host=psql_host,
302
307
  port=psql_port,
308
+ database=psql_database,
303
309
  ),
304
310
  ">",
305
311
  QuoteString(dest),
@@ -309,18 +315,17 @@ def dump(
309
315
  @operation(is_idempotent=False)
310
316
  def load(
311
317
  src: str,
312
- database: str | None = None,
313
318
  # Details for speaking to PostgreSQL via `psql` CLI
314
319
  psql_user: str | None = None,
315
320
  psql_password: str | None = None,
316
321
  psql_host: str | None = None,
317
322
  psql_port: int | None = None,
323
+ psql_database: str | None = None,
318
324
  ):
319
325
  """
320
326
  Load ``.sql`` file into a database.
321
327
 
322
328
  + src: the filename to read from
323
- + database: name of the database to import into
324
329
  + psql_*: global module arguments, see above
325
330
 
326
331
  **Example:**
@@ -330,7 +335,6 @@ def load(
330
335
  postgresql.load(
331
336
  name="Import the pyinfra_stuff dump into pyinfra_stuff_copy",
332
337
  src="/tmp/pyinfra_stuff.dump",
333
- database="pyinfra_stuff_copy",
334
338
  sudo_user="postgres",
335
339
  )
336
340
 
@@ -338,11 +342,11 @@ def load(
338
342
 
339
343
  yield StringCommand(
340
344
  make_psql_command(
341
- database=database,
342
345
  user=psql_user,
343
346
  password=psql_password,
344
347
  host=psql_host,
345
348
  port=psql_port,
349
+ database=psql_database,
346
350
  ),
347
351
  "<",
348
352
  QuoteString(src),
@@ -2,6 +2,8 @@
2
2
  Manage runit services.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  from typing import Optional
6
8
 
7
9
  from pyinfra import host
@@ -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
@@ -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()
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.1.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'