check-python-versions 0.23.0__tar.gz → 0.24.1__tar.gz

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 (64) hide show
  1. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/CHANGES.rst +20 -0
  2. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/PKG-INFO +4 -7
  3. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/README.rst +1 -1
  4. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/setup.py +2 -5
  5. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/__init__.py +1 -1
  6. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/cli.py +19 -17
  7. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/classifiers.py +2 -2
  8. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/ini.py +1 -2
  9. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/poetry_version_spec.py +6 -6
  10. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/python.py +5 -6
  11. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/requires_python.py +6 -6
  12. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/yaml.py +8 -8
  13. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/appveyor.py +4 -4
  14. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/base.py +7 -7
  15. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/github.py +3 -3
  16. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/pyproject.py +8 -8
  17. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/setup_py.py +6 -6
  18. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/tox.py +14 -10
  19. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/travis.py +3 -5
  20. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/utils.py +6 -16
  21. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/versions.py +6 -6
  22. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/PKG-INFO +4 -7
  23. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_classifiers.py +1 -3
  24. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_poetry_version_spec.py +1 -3
  25. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_requires_python.py +1 -3
  26. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_appveyor.py +1 -2
  27. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_github.py +1 -2
  28. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_manylinux.py +1 -2
  29. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_pyproject.py +2 -2
  30. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_setup_py.py +1 -2
  31. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_tox.py +9 -3
  32. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/sources/test_travis.py +1 -2
  33. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/test_cli.py +5 -4
  34. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tox.ini +38 -2
  35. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/.coveragerc +0 -0
  36. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/.gitignore +0 -0
  37. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/.pre-commit-hooks.yaml +0 -0
  38. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/CLASSIFIERS +0 -0
  39. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/LICENSE +0 -0
  40. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/MANIFEST.in +0 -0
  41. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/Makefile +0 -0
  42. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/check-python-versions +0 -0
  43. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/pytest.ini +0 -0
  44. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/release.mk +0 -0
  45. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/setup.cfg +0 -0
  46. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/__main__.py +0 -0
  47. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/parsers/__init__.py +0 -0
  48. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/__init__.py +0 -0
  49. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/all.py +0 -0
  50. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions/sources/manylinux.py +0 -0
  51. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/SOURCES.txt +0 -0
  52. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/dependency_links.txt +0 -0
  53. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/entry_points.txt +0 -0
  54. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/not-zip-safe +0 -0
  55. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/requires.txt +0 -0
  56. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/src/check_python_versions.egg-info/top_level.txt +0 -0
  57. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/__init__.py +0 -0
  58. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/conftest.py +0 -0
  59. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_ini.py +0 -0
  60. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_python.py +0 -0
  61. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/parsers/test_yaml.py +0 -0
  62. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/test___main__.py +0 -0
  63. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/test_utils.py +0 -0
  64. {check_python_versions-0.23.0 → check_python_versions-0.24.1}/tests/test_versions.py +0 -0
@@ -1,6 +1,26 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 0.24.1 (2026-04-14)
5
+ -------------------
6
+
7
+ - Improve tox environment name parsing: accept dotted names (e.g. py3.14),
8
+ more specific pypy versions (pypy311). `GH #38
9
+ <https://github.com/mgedmin/check-python-versions/issues/38>`_ and `GH #50
10
+ <https://github.com/mgedmin/check-python-versions/issues/50>`_.
11
+
12
+
13
+ 0.24.0 (2025-11-13)
14
+ -------------------
15
+
16
+ - Drop support for Python 3.8 and 3.9.
17
+
18
+ - Mention the ``--allow-non-packages`` option when showing the error message
19
+ that rejects non-packages.
20
+
21
+ - Add ``--set`` as an alias for ``--update``.
22
+
23
+
4
24
  0.23.0 (2025-10-14)
5
25
  -------------------
6
26
 
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: check-python-versions
3
- Version: 0.23.0
3
+ Version: 0.24.1
4
4
  Summary: Compare supported Python versions in setup.py vs tox.ini et al.
5
5
  Home-page: https://github.com/mgedmin/check-python-versions
6
6
  Author: Marius Gedminas
7
7
  Author-email: marius@gedmin.as
8
- License: GPL
8
+ License: GPL-3.0-or-later
9
9
  Project-URL: Changelog, https://github.com/mgedmin/check-python-versions/blob/master/CHANGES.rst
10
10
  Project-URL: Issues, https://github.com/mgedmin/check-python-versions/issues
11
11
  Project-URL: Source Code, https://github.com/mgedmin/check-python-versions
@@ -13,10 +13,7 @@ Keywords: python packaging version checker linter setup.py tox travis appveyor
13
13
  Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Environment :: Console
15
15
  Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: GNU General Public License (GPL)
17
16
  Classifier: Operating System :: OS Independent
18
- Classifier: Programming Language :: Python :: 3.8
19
- Classifier: Programming Language :: Python :: 3.9
20
17
  Classifier: Programming Language :: Python :: 3.10
21
18
  Classifier: Programming Language :: Python :: 3.11
22
19
  Classifier: Programming Language :: Python :: 3.12
@@ -24,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.13
24
21
  Classifier: Programming Language :: Python :: 3.14
25
22
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
23
  Classifier: Programming Language :: Python :: Implementation :: PyPy
27
- Requires-Python: >=3.8
24
+ Requires-Python: >=3.10
28
25
  Description-Content-Type: text/x-rst
29
26
  License-File: LICENSE
30
27
  Requires-Dist: pyyaml
@@ -429,6 +426,6 @@ Add the following snippet to your ``.pre-commit-config.yaml``.
429
426
 
430
427
  repos:
431
428
  - repo: https://github.com/mgedmin/check-python-versions
432
- rev: "0.23.0"
429
+ rev: "0.24.1"
433
430
  hooks:
434
431
  - id: check-python-versions
@@ -384,6 +384,6 @@ Add the following snippet to your ``.pre-commit-config.yaml``.
384
384
 
385
385
  repos:
386
386
  - repo: https://github.com/mgedmin/check-python-versions
387
- rev: "0.23.0"
387
+ rev: "0.24.1"
388
388
  hooks:
389
389
  - id: check-python-versions
@@ -49,10 +49,7 @@ setup(
49
49
  'Development Status :: 4 - Beta',
50
50
  'Environment :: Console',
51
51
  'Intended Audience :: Developers',
52
- 'License :: OSI Approved :: GNU General Public License (GPL)',
53
52
  'Operating System :: OS Independent',
54
- 'Programming Language :: Python :: 3.8',
55
- 'Programming Language :: Python :: 3.9',
56
53
  'Programming Language :: Python :: 3.10',
57
54
  'Programming Language :: Python :: 3.11',
58
55
  'Programming Language :: Python :: 3.12',
@@ -61,8 +58,8 @@ setup(
61
58
  'Programming Language :: Python :: Implementation :: CPython',
62
59
  'Programming Language :: Python :: Implementation :: PyPy',
63
60
  ],
64
- license='GPL',
65
- python_requires=">=3.8",
61
+ license='GPL-3.0-or-later',
62
+ python_requires=">=3.10",
66
63
  packages=find_packages('src'),
67
64
  package_dir={'': 'src'},
68
65
  entry_points={
@@ -13,4 +13,4 @@ Makes sure the set of supported Python versions is consistent between
13
13
  """
14
14
 
15
15
  __author__ = 'Marius Gedminas <marius@gedmin.as>'
16
- __version__ = '0.23.0'
16
+ __version__ = '0.24.1'
@@ -12,7 +12,7 @@ import glob
12
12
  import os
13
13
  import sys
14
14
  from io import StringIO
15
- from typing import Callable, Collection, Dict, List, Optional, Tuple
15
+ from typing import Callable, Collection
16
16
 
17
17
  from . import __version__
18
18
  from .sources.all import ALL_SOURCES
@@ -34,7 +34,7 @@ from .versions import (
34
34
  )
35
35
 
36
36
 
37
- def parse_version(v: str) -> Tuple[int, int]:
37
+ def parse_version(v: str) -> tuple[int, int]:
38
38
  """Parse a Python version number.
39
39
 
40
40
  Expects 'MAJOR.MINOR', no more, no less.
@@ -127,6 +127,7 @@ def check_package(where: str = '.', *, print: PrintFn = print) -> bool:
127
127
 
128
128
  if not is_package(where):
129
129
  print("no setup.py or pyproject.toml -- not a Python package?")
130
+ print("you may want to try --allow-non-packages")
130
131
  return False
131
132
 
132
133
  return True
@@ -147,11 +148,11 @@ def check_package(where: str = '.', *, print: PrintFn = print) -> bool:
147
148
  # results, to get back the final results.
148
149
  #
149
150
 
150
- ReplacementDict = Dict[str, FileLines]
151
+ ReplacementDict = dict[str, FileLines]
151
152
 
152
153
 
153
154
  def filename_or_replacement(
154
- pathname: str, replacements: Optional[ReplacementDict]
155
+ pathname: str, replacements: ReplacementDict | None
155
156
  ) -> FileOrFilename:
156
157
  """Look up a file in the replacement dict.
157
158
 
@@ -175,10 +176,10 @@ FilenameSet = Collection[str]
175
176
  def find_sources(
176
177
  where: str = '.',
177
178
  *,
178
- replacements: Optional[ReplacementDict] = None,
179
- only: Optional[FilenameSet] = None,
179
+ replacements: ReplacementDict | None = None,
180
+ only: FilenameSet | None = None,
180
181
  supports_update: bool = False,
181
- ) -> List[SourceFile]:
182
+ ) -> list[SourceFile]:
182
183
  """Find all sources that exist in a given directory.
183
184
 
184
185
  ``replacements`` allows you to check the result of an update (see
@@ -211,9 +212,9 @@ def check_versions(
211
212
  *,
212
213
  print: PrintFn = print,
213
214
  min_width: int = 0,
214
- expect: Optional[VersionList] = None,
215
- replacements: Optional[ReplacementDict] = None,
216
- only: Optional[FilenameSet] = None,
215
+ expect: VersionList | None = None,
216
+ replacements: ReplacementDict | None = None,
217
+ only: FilenameSet | None = None,
217
218
  ) -> bool:
218
219
  """Check Python versions for a single package, located in ``where``.
219
220
 
@@ -251,8 +252,8 @@ def check_versions(
251
252
 
252
253
 
253
254
  def supported_versions_match(
254
- sources: List[SourceFile],
255
- expect: Optional[VersionList] = None,
255
+ sources: list[SourceFile],
256
+ expect: VersionList | None = None,
256
257
  ) -> bool:
257
258
  version_sets = []
258
259
  pypy_version_sets = []
@@ -291,12 +292,12 @@ def supported_versions_match(
291
292
  def update_versions(
292
293
  where: str = '.',
293
294
  *,
294
- add: Optional[VersionList] = None,
295
- drop: Optional[VersionList] = None,
296
- update: Optional[VersionList] = None,
295
+ add: VersionList | None = None,
296
+ drop: VersionList | None = None,
297
+ update: VersionList | None = None,
297
298
  diff: bool = False,
298
299
  dry_run: bool = False,
299
- only: Optional[FilenameSet] = None,
300
+ only: FilenameSet | None = None,
300
301
  ) -> ReplacementDict:
301
302
  """Update Python versions for a single package, located in ``where``.
302
303
 
@@ -389,7 +390,8 @@ def _main() -> None:
389
390
  group.add_argument('--drop', metavar='VERSIONS', type=parse_version_list,
390
391
  help='drop these versions from supported ones, e.g'
391
392
  ' --drop 2.6,3.4')
392
- group.add_argument('--update', metavar='VERSIONS', type=parse_version_list,
393
+ group.add_argument('--update', '--set', metavar='VERSIONS',
394
+ type=parse_version_list,
393
395
  help='update the set of supported versions, e.g.'
394
396
  ' --update 2.7,3.5-3.7')
395
397
  group.add_argument('--diff', action='store_true',
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Tools for manipulating PyPI classifiers.
3
3
  """
4
- from typing import List, Sequence
4
+ from typing import Sequence
5
5
 
6
6
  from ..versions import SortedVersionList, Version, expand_pypy
7
7
 
@@ -52,7 +52,7 @@ def get_versions_from_classifiers(
52
52
  def update_classifiers(
53
53
  classifiers: Sequence[str],
54
54
  new_versions: SortedVersionList
55
- ) -> List[str]:
55
+ ) -> list[str]:
56
56
  """Update a list of classifiers with new Python versions."""
57
57
  prefix = 'Programming Language :: Python :: '
58
58
 
@@ -6,7 +6,6 @@ INI parser and serializer.
6
6
  """
7
7
 
8
8
  import re
9
- from typing import List
10
9
 
11
10
  from ..utils import FileLines, get_indent, warn
12
11
 
@@ -55,7 +54,7 @@ def update_ini_setting(
55
54
 
56
55
  end = start + 1
57
56
  comments = []
58
- pending_comments: List[str] = []
57
+ pending_comments: list[str] = []
59
58
  indent = ' '
60
59
  for n, line in lines:
61
60
  if line.startswith(' '):
@@ -6,7 +6,7 @@ https://python-poetry.org/docs/dependency-specification/#version-constraints
6
6
  """
7
7
 
8
8
  import re
9
- from typing import Callable, Dict, List, Optional, Tuple, TypedDict, Union
9
+ from typing import Callable, TypedDict
10
10
 
11
11
  from ..utils import warn
12
12
  from ..versions import (
@@ -22,7 +22,7 @@ def parse_poetry_version_constraint(
22
22
  name: str = "tool.poetry.dependencies.python",
23
23
  *,
24
24
  filename: str = 'pyproject.toml',
25
- ) -> Optional[SortedVersionList]:
25
+ ) -> SortedVersionList | None:
26
26
  """Compute Python versions allowed by Poetry version constraints."""
27
27
 
28
28
  rx = re.compile(r'^(|[~^]|==|!=|<=|>=|<|>)\s*(\d+(?:\.\d+)*(?:\.\*)?)$')
@@ -36,7 +36,7 @@ def parse_poetry_version_constraint(
36
36
  # with a possible trailing '*'. PEP 440 calls them "clauses".
37
37
  #
38
38
 
39
- Constraint = Tuple[Union[str, int], ...]
39
+ Constraint = tuple[str | int, ...]
40
40
 
41
41
  #
42
42
  # The we look up a handler for each operartor. This handler takes a
@@ -45,7 +45,7 @@ def parse_poetry_version_constraint(
45
45
  # that version passes its constraint.
46
46
  #
47
47
 
48
- VersionTuple = Tuple[int, int]
48
+ VersionTuple = tuple[int, int]
49
49
  CheckFn = Callable[[VersionTuple], bool]
50
50
  HandlerFn = Callable[[Constraint], CheckFn]
51
51
 
@@ -53,7 +53,7 @@ def parse_poetry_version_constraint(
53
53
  # Here we're defining the handlers for all the operators
54
54
  #
55
55
 
56
- handlers: Dict[str, HandlerFn] = {}
56
+ handlers: dict[str, HandlerFn] = {}
57
57
 
58
58
  def handler(operator: str) -> Callable[[HandlerFn], None]:
59
59
  def decorator(fn: HandlerFn) -> None:
@@ -186,7 +186,7 @@ def parse_poetry_version_constraint(
186
186
  # into checkers (which I also call "constraints", for maximum confusion).
187
187
  #
188
188
 
189
- constraints: List[CheckFn] = []
189
+ constraints: list[CheckFn] = []
190
190
  for specifier in map(str.strip, s.split(',')):
191
191
  m = rx.match(specifier)
192
192
  if not m:
@@ -5,12 +5,11 @@ Tools for manipulating Python files.
5
5
  import ast
6
6
  import re
7
7
  import string
8
- from typing import List, Optional, Tuple, Union
9
8
 
10
9
  from ..utils import FileLines, OneOrMore, get_indent, warn
11
10
 
12
11
 
13
- AstValue = Union[str, List[str], Tuple[str, ...]]
12
+ AstValue = str | list[str] | tuple[str, ...]
14
13
 
15
14
 
16
15
  def to_literal(value: str, quote_style: str = '"') -> str:
@@ -33,7 +32,7 @@ def update_call_arg_in_source(
33
32
  source_lines: FileLines,
34
33
  function: OneOrMore[str],
35
34
  keyword: str,
36
- new_value: Union[str, List[str]],
35
+ new_value: str | list[str],
37
36
  *,
38
37
  filename: str = 'setup.py',
39
38
  ) -> FileLines:
@@ -169,7 +168,7 @@ def find_call_kwarg_in_ast(
169
168
  keyword: str,
170
169
  *,
171
170
  filename: str,
172
- ) -> Optional[ast.AST]:
171
+ ) -> ast.AST | None:
173
172
  """Find the value passed to a function call.
174
173
 
175
174
  ``filename`` is used for error reporting.
@@ -204,7 +203,7 @@ def eval_ast_node(
204
203
  keyword: str,
205
204
  *,
206
205
  filename: str = 'setup.py',
207
- ) -> Optional[AstValue]:
206
+ ) -> AstValue | None:
208
207
  """Partially evaluate an AST node.
209
208
 
210
209
  ``keyword`` is used for error reporting.
@@ -212,7 +211,7 @@ def eval_ast_node(
212
211
  if isinstance(node, ast.Constant) and isinstance(node.value, str):
213
212
  return node.value
214
213
  if isinstance(node, (ast.List, ast.Tuple)):
215
- values: List[str] = []
214
+ values: list[str] = []
216
215
  warned = False
217
216
  for element in node.elts:
218
217
  try:
@@ -2,7 +2,7 @@
2
2
  Tools for manipulating requires-python PyPI classifiers.
3
3
  """
4
4
  import re
5
- from typing import Callable, Dict, List, Optional, Tuple, TypedDict, Union
5
+ from typing import Callable, TypedDict
6
6
 
7
7
  from ..utils import warn
8
8
  from ..versions import (
@@ -18,7 +18,7 @@ def parse_python_requires(
18
18
  name: str = "python_requires",
19
19
  *,
20
20
  filename: str = "setup.py",
21
- ) -> Optional[SortedVersionList]:
21
+ ) -> SortedVersionList | None:
22
22
  """Compute Python versions allowed by a python_requires expression."""
23
23
 
24
24
  # https://www.python.org/dev/peps/pep-0440/#version-specifiers
@@ -33,7 +33,7 @@ def parse_python_requires(
33
33
  # with a possible trailing '*'. PEP 440 calls them "clauses".
34
34
  #
35
35
 
36
- Constraint = Tuple[Union[str, int], ...]
36
+ Constraint = tuple[str | int, ...]
37
37
 
38
38
  #
39
39
  # The we look up a handler for each operartor. This handler takes a
@@ -42,7 +42,7 @@ def parse_python_requires(
42
42
  # that version passes its constraint.
43
43
  #
44
44
 
45
- VersionTuple = Tuple[int, int]
45
+ VersionTuple = tuple[int, int]
46
46
  CheckFn = Callable[[VersionTuple], bool]
47
47
  HandlerFn = Callable[[Constraint], CheckFn]
48
48
 
@@ -50,7 +50,7 @@ def parse_python_requires(
50
50
  # Here we're defining the handlers for all the operators
51
51
  #
52
52
 
53
- handlers: Dict[str, HandlerFn] = {}
53
+ handlers: dict[str, HandlerFn] = {}
54
54
 
55
55
  def handler(operator: str) -> Callable[[HandlerFn], None]:
56
56
  def decorator(fn: HandlerFn) -> None:
@@ -166,7 +166,7 @@ def parse_python_requires(
166
166
  # into checkers (which I also call "constraints", for maximum confusion).
167
167
  #
168
168
 
169
- constraints: List[CheckFn] = []
169
+ constraints: list[CheckFn] = []
170
170
  for specifier in map(str.strip, s.split(',')):
171
171
  m = rx.match(specifier)
172
172
  if not m:
@@ -6,7 +6,7 @@ YAML parser and serializer.
6
6
  """
7
7
 
8
8
  import string
9
- from typing import Any, Callable, Dict, List, Optional
9
+ from typing import Any, Callable
10
10
 
11
11
  from ..utils import FileLines, OneOrMore, OneOrTuple, warn
12
12
 
@@ -34,11 +34,11 @@ def quote_string(value: str, quote_style: str = '') -> str:
34
34
  def update_yaml_list(
35
35
  orig_lines: FileLines,
36
36
  key: OneOrTuple[str],
37
- new_value: List[Any],
37
+ new_value: list[Any],
38
38
  *,
39
39
  filename: str,
40
- keep: Optional[Callable[[str], bool]] = None,
41
- replacements: Optional[Dict[str, str]] = None,
40
+ keep: Callable[[str], bool] | None = None,
41
+ replacements: dict[str, str] | None = None,
42
42
  ) -> FileLines:
43
43
  """Update a list of values in a YAML document.
44
44
 
@@ -94,10 +94,10 @@ def update_yaml_list(
94
94
  end = n + 1
95
95
  indent = 2
96
96
  list_indent = None
97
- keep_before: List[str] = []
98
- keep_after: List[str] = []
97
+ keep_before: list[str] = []
98
+ keep_after: list[str] = []
99
99
  lines_to_keep = keep_before
100
- kept_last: Optional[bool] = False
100
+ kept_last: bool | None = False
101
101
  for n, line in lines:
102
102
  stripped = line.lstrip()
103
103
  line_indent = len(line) - len(stripped)
@@ -187,7 +187,7 @@ def add_yaml_node(
187
187
  key: str,
188
188
  value: str,
189
189
  *,
190
- before: Optional[OneOrMore[str]] = None,
190
+ before: OneOrMore[str] | None = None,
191
191
  ) -> FileLines:
192
192
  """Add a value to a YAML document.
193
193
 
@@ -24,7 +24,7 @@ of Tox environments (pyXY).
24
24
 
25
25
  import ast
26
26
  from io import StringIO
27
- from typing import Optional, Set, cast
27
+ from typing import Set, cast
28
28
 
29
29
  import yaml
30
30
 
@@ -61,7 +61,7 @@ def get_appveyor_yml_python_versions(
61
61
  return sorted(cast(Set[Version], set(versions) - {None}))
62
62
 
63
63
 
64
- def appveyor_normalize_py_version(ver: str) -> Optional[Version]:
64
+ def appveyor_normalize_py_version(ver: str) -> Version | None:
65
65
  """Determine Python version from PYTHON environment variable."""
66
66
  ver = str(ver).lower().replace('\\', '/')
67
67
  if ver.startswith('c:/python'):
@@ -78,7 +78,7 @@ def appveyor_normalize_py_version(ver: str) -> Optional[Version]:
78
78
  return None
79
79
 
80
80
 
81
- def appveyor_detect_py_version_pattern(ver: str) -> Optional[str]:
81
+ def appveyor_detect_py_version_pattern(ver: str) -> str | None:
82
82
  """Determine the format of the PYTHON environment variable.
83
83
 
84
84
  Returns a format string suitable for formatting with placeholders
@@ -113,7 +113,7 @@ def escape(s: str) -> str:
113
113
  def update_appveyor_yml_python_versions(
114
114
  filename: FileOrFilename,
115
115
  new_versions: VersionList,
116
- ) -> Optional[FileLines]:
116
+ ) -> FileLines | None:
117
117
  """Update supported Python versions in appveyor.yml.
118
118
 
119
119
  Does not touch the file but returns a list of lines with new file contents.
@@ -1,11 +1,11 @@
1
- from typing import Callable, Optional
1
+ from typing import Callable
2
2
 
3
3
  from ..utils import FileLines, FileOrFilename
4
4
  from ..versions import SortedVersionList
5
5
 
6
6
 
7
- ExtractorFn = Callable[[FileOrFilename], Optional[SortedVersionList]]
8
- UpdaterFn = Callable[[FileOrFilename, SortedVersionList], Optional[FileLines]]
7
+ ExtractorFn = Callable[[FileOrFilename], SortedVersionList | None]
8
+ UpdaterFn = Callable[[FileOrFilename, SortedVersionList], FileLines | None]
9
9
 
10
10
 
11
11
  class Source:
@@ -26,10 +26,10 @@ class Source:
26
26
  def __init__(
27
27
  self,
28
28
  *,
29
- title: Optional[str] = None,
29
+ title: str | None = None,
30
30
  filename: str,
31
31
  extract: ExtractorFn,
32
- update: Optional[UpdaterFn] = None,
32
+ update: UpdaterFn | None = None,
33
33
  check_pypy_consistency: bool,
34
34
  has_upper_bound: bool,
35
35
  ) -> None:
@@ -70,10 +70,10 @@ class SourceFile(Source):
70
70
  def __init__(
71
71
  self,
72
72
  *,
73
- title: Optional[str] = None,
73
+ title: str | None = None,
74
74
  filename: str,
75
75
  extract: ExtractorFn,
76
- update: Optional[UpdaterFn] = None,
76
+ update: UpdaterFn | None = None,
77
77
  check_pypy_consistency: bool,
78
78
  has_upper_bound: bool,
79
79
  pathname: str,
@@ -9,7 +9,7 @@ simplifying assumptions:
9
9
  - on 'config' that contains lists of [python_version, tox_env]
10
10
  """
11
11
 
12
- from typing import Optional, Set, Union
12
+ from typing import Set
13
13
 
14
14
  import yaml
15
15
 
@@ -26,7 +26,7 @@ GHA_WORKFLOW_GLOB = '.github/workflows/*.yml'
26
26
 
27
27
  def get_gha_python_versions(
28
28
  filename: FileOrFilename = GHA_WORKFLOW_FILE,
29
- ) -> Optional[SortedVersionList]:
29
+ ) -> SortedVersionList | None:
30
30
  """Extract supported Python versions from a GitHub workflow."""
31
31
  with open_file(filename) as fp:
32
32
  conf = yaml.safe_load(fp)
@@ -57,7 +57,7 @@ def get_gha_python_versions(
57
57
  return sorted(set(versions))
58
58
 
59
59
 
60
- def parse_gh_ver(v: Union[str, float]) -> Version:
60
+ def parse_gh_ver(v: str | float) -> Version:
61
61
  """Parse Python versions used for actions/setup-python@v2.
62
62
 
63
63
  This format is not fully well documented. There's support for
@@ -33,7 +33,7 @@ check-python-versions also supports old-style Flit and Poetry metadata::
33
33
  python = "^3.8"
34
34
 
35
35
  """
36
- from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
36
+ from typing import TYPE_CHECKING, Any
37
37
 
38
38
  import tomlkit
39
39
  from tomlkit import TOMLDocument, dumps, load
@@ -68,7 +68,7 @@ if TYPE_CHECKING:
68
68
 
69
69
 
70
70
  def traverse(document: TOMLDocument, path: str, default: Any = None) -> Any:
71
- obj: Union[Container, Item] = document
71
+ obj: Container | Item = document
72
72
  for step in path.split('.'):
73
73
  if not isinstance(obj, dict):
74
74
  # complain
@@ -81,7 +81,7 @@ def traverse(document: TOMLDocument, path: str, default: Any = None) -> Any:
81
81
 
82
82
  def _get_pyproject_toml_classifiers(
83
83
  filename: FileOrFilename = PYPROJECT_TOML,
84
- ) -> Tuple[TOMLDocument, str, Optional[List[str]]]:
84
+ ) -> tuple[TOMLDocument, str, list[str] | None]:
85
85
  """Extract the list of PyPI classifiers from a pyproject.toml"""
86
86
 
87
87
  with open_file(filename) as fp:
@@ -116,7 +116,7 @@ def _get_pyproject_toml_classifiers(
116
116
 
117
117
  def get_supported_python_versions(
118
118
  filename: FileOrFilename = PYPROJECT_TOML,
119
- ) -> Optional[SortedVersionList]:
119
+ ) -> SortedVersionList | None:
120
120
  """Extract supported Python versions from classifiers in pyproject.toml."""
121
121
 
122
122
  _d, _p, classifiers = _get_pyproject_toml_classifiers(filename)
@@ -129,7 +129,7 @@ def get_supported_python_versions(
129
129
 
130
130
  def _get_pyproject_toml_requires_python(
131
131
  filename: FileOrFilename = PYPROJECT_TOML,
132
- ) -> Tuple[TOMLDocument, str, Optional[str]]:
132
+ ) -> tuple[TOMLDocument, str, str | None]:
133
133
 
134
134
  with open_file(filename) as fp:
135
135
  document = load(fp)
@@ -155,7 +155,7 @@ def _get_pyproject_toml_requires_python(
155
155
 
156
156
  def get_python_requires(
157
157
  filename: FileOrFilename = PYPROJECT_TOML,
158
- ) -> Optional[SortedVersionList]:
158
+ ) -> SortedVersionList | None:
159
159
  """Extract Python versions from require-python in pyproject.toml."""
160
160
 
161
161
  _d, path, python_requires = _get_pyproject_toml_requires_python(filename)
@@ -174,7 +174,7 @@ def get_python_requires(
174
174
  def update_supported_python_versions(
175
175
  filename: FileOrFilename,
176
176
  new_versions: SortedVersionList,
177
- ) -> Optional[FileLines]:
177
+ ) -> FileLines | None:
178
178
  """Update classifiers in a pyproject.toml.
179
179
 
180
180
  Does not touch the file but returns a list of lines with new file contents.
@@ -197,7 +197,7 @@ def update_supported_python_versions(
197
197
  def update_python_requires(
198
198
  filename: FileOrFilename,
199
199
  new_versions: SortedVersionList,
200
- ) -> Optional[FileLines]:
200
+ ) -> FileLines | None:
201
201
  """Update python dependency in a pyproject.toml, if it's defined there.
202
202
 
203
203
  Does not touch the file but returns a list of lines with new file contents.
@@ -15,7 +15,7 @@ import ast
15
15
  import os
16
16
  import shutil
17
17
  import sys
18
- from typing import List, Optional, TextIO, Union, cast
18
+ from typing import TextIO, cast
19
19
 
20
20
  from .base import Source
21
21
  from ..parsers.classifiers import (
@@ -77,7 +77,7 @@ def get_supported_python_versions(
77
77
 
78
78
  def get_python_requires(
79
79
  setup_py: FileOrFilename = SETUP_PY,
80
- ) -> Optional[SortedVersionList]:
80
+ ) -> SortedVersionList | None:
81
81
  """Extract supported Python versions from python_requires in setup.py."""
82
82
  python_requires = get_setup_py_keyword(setup_py, 'python_requires')
83
83
  if python_requires is None:
@@ -92,7 +92,7 @@ def get_python_requires(
92
92
  def update_supported_python_versions(
93
93
  filename: FileOrFilename,
94
94
  new_versions: SortedVersionList,
95
- ) -> Optional[FileLines]:
95
+ ) -> FileLines | None:
96
96
  """Update classifiers in a setup.py.
97
97
 
98
98
  Does not touch the file but returns a list of lines with new file contents.
@@ -111,7 +111,7 @@ def update_supported_python_versions(
111
111
  def update_python_requires(
112
112
  filename: FileOrFilename,
113
113
  new_versions: SortedVersionList,
114
- ) -> Optional[FileLines]:
114
+ ) -> FileLines | None:
115
115
  """Update python_requires in a setup.py, if it's defined there.
116
116
 
117
117
  Does not touch the file but returns a list of lines with new file contents.
@@ -132,7 +132,7 @@ def update_python_requires(
132
132
  def get_setup_py_keyword(
133
133
  setup_py: FileOrFilename,
134
134
  keyword: str,
135
- ) -> Optional[AstValue]:
135
+ ) -> AstValue | None:
136
136
  """Extract a value passed to setup() in a setup.py.
137
137
 
138
138
  Parses the setup.py into an Abstact Syntax Tree and tries to figure out
@@ -156,7 +156,7 @@ def get_setup_py_keyword(
156
156
  def update_setup_py_keyword(
157
157
  setup_py: FileOrFilename,
158
158
  keyword: str,
159
- new_value: Union[str, List[str]],
159
+ new_value: str | list[str],
160
160
  ) -> FileLines:
161
161
  """Update a value passed to setup() in a setup.py.
162
162