Skip to content

Commit cc938f9

Browse files
authored
Modernise type annotations using FA rules from ruff (#785)
* Modernise type annotations using `FA` rules from ruff This enforces the use of `from __future__ import annotations` on all files that use objects from `typing` that have clearer syntax available in newer typing annotation syntax. * Use recursive types in `_parser` This makes use of more accurate type checking enabled by newer type checker versions. --------- Co-authored-by: Pradyun Gedam <pradyunsg@users.noreply.github.com>
1 parent 757f559 commit cc938f9

14 files changed

+204
-209
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ extend-select = [
6565
"B",
6666
"E",
6767
"F",
68+
"FA",
6869
"I",
6970
"N",
7071
"UP",

src/packaging/_elffile.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
99
"""
1010

11+
from __future__ import annotations
12+
1113
import enum
1214
import os
1315
import struct
14-
from typing import IO, Optional, Tuple
16+
from typing import IO
1517

1618

1719
class ELFInvalid(ValueError):
@@ -87,11 +89,11 @@ def __init__(self, f: IO[bytes]) -> None:
8789
except struct.error as e:
8890
raise ELFInvalid("unable to parse machine and section information") from e
8991

90-
def _read(self, fmt: str) -> Tuple[int, ...]:
92+
def _read(self, fmt: str) -> tuple[int, ...]:
9193
return struct.unpack(fmt, self._f.read(struct.calcsize(fmt)))
9294

9395
@property
94-
def interpreter(self) -> Optional[str]:
96+
def interpreter(self) -> str | None:
9597
"""
9698
The path recorded in the ``PT_INTERP`` section header.
9799
"""

src/packaging/_manylinux.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from __future__ import annotations
2+
13
import collections
24
import contextlib
35
import functools
46
import os
57
import re
68
import sys
79
import warnings
8-
from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple
10+
from typing import Generator, Iterator, NamedTuple, Sequence
911

1012
from ._elffile import EIClass, EIData, ELFFile, EMachine
1113

@@ -17,7 +19,7 @@
1719
# `os.PathLike` not a generic type until Python 3.9, so sticking with `str`
1820
# as the type for `path` until then.
1921
@contextlib.contextmanager
20-
def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]:
22+
def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]:
2123
try:
2224
with open(path, "rb") as f:
2325
yield ELFFile(f)
@@ -72,15 +74,15 @@ def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool:
7274
# For now, guess what the highest minor version might be, assume it will
7375
# be 50 for testing. Once this actually happens, update the dictionary
7476
# with the actual value.
75-
_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50)
77+
_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50)
7678

7779

7880
class _GLibCVersion(NamedTuple):
7981
major: int
8082
minor: int
8183

8284

83-
def _glibc_version_string_confstr() -> Optional[str]:
85+
def _glibc_version_string_confstr() -> str | None:
8486
"""
8587
Primary implementation of glibc_version_string using os.confstr.
8688
"""
@@ -90,7 +92,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
9092
# https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
9193
try:
9294
# Should be a string like "glibc 2.17".
93-
version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION")
95+
version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION")
9496
assert version_string is not None
9597
_, version = version_string.rsplit()
9698
except (AssertionError, AttributeError, OSError, ValueError):
@@ -99,7 +101,7 @@ def _glibc_version_string_confstr() -> Optional[str]:
99101
return version
100102

101103

102-
def _glibc_version_string_ctypes() -> Optional[str]:
104+
def _glibc_version_string_ctypes() -> str | None:
103105
"""
104106
Fallback implementation of glibc_version_string using ctypes.
105107
"""
@@ -143,12 +145,12 @@ def _glibc_version_string_ctypes() -> Optional[str]:
143145
return version_str
144146

145147

146-
def _glibc_version_string() -> Optional[str]:
148+
def _glibc_version_string() -> str | None:
147149
"""Returns glibc version string, or None if not using glibc."""
148150
return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
149151

150152

151-
def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
153+
def _parse_glibc_version(version_str: str) -> tuple[int, int]:
152154
"""Parse glibc version.
153155
154156
We use a regexp instead of str.split because we want to discard any
@@ -168,7 +170,7 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
168170

169171

170172
@functools.lru_cache
171-
def _get_glibc_version() -> Tuple[int, int]:
173+
def _get_glibc_version() -> tuple[int, int]:
172174
version_str = _glibc_version_string()
173175
if version_str is None:
174176
return (-1, -1)

src/packaging/_musllinux.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
linked against musl, and what musl version is used.
55
"""
66

7+
from __future__ import annotations
8+
79
import functools
810
import re
911
import subprocess
1012
import sys
11-
from typing import Iterator, NamedTuple, Optional, Sequence
13+
from typing import Iterator, NamedTuple, Sequence
1214

1315
from ._elffile import ELFFile
1416

@@ -18,7 +20,7 @@ class _MuslVersion(NamedTuple):
1820
minor: int
1921

2022

21-
def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
23+
def _parse_musl_version(output: str) -> _MuslVersion | None:
2224
lines = [n for n in (n.strip() for n in output.splitlines()) if n]
2325
if len(lines) < 2 or lines[0][:4] != "musl":
2426
return None
@@ -29,7 +31,7 @@ def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
2931

3032

3133
@functools.lru_cache
32-
def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
34+
def _get_musl_version(executable: str) -> _MuslVersion | None:
3335
"""Detect currently-running musl runtime version.
3436
3537
This is done by checking the specified executable's dynamic linking

src/packaging/_parser.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
the implementation.
55
"""
66

7+
from __future__ import annotations
8+
79
import ast
8-
from typing import Any, List, NamedTuple, Optional, Tuple, Union
10+
from typing import NamedTuple, Sequence, Tuple, Union
911

1012
from ._tokenizer import DEFAULT_RULES, Tokenizer
1113

@@ -41,20 +43,16 @@ def serialize(self) -> str:
4143

4244
MarkerVar = Union[Variable, Value]
4345
MarkerItem = Tuple[MarkerVar, Op, MarkerVar]
44-
# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]]
45-
# MarkerList = List[Union["MarkerList", MarkerAtom, str]]
46-
# mypy does not support recursive type definition
47-
# https://github.com/python/mypy/issues/731
48-
MarkerAtom = Any
49-
MarkerList = List[Any]
46+
MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]]
47+
MarkerList = Sequence[Union["MarkerList", MarkerAtom, str]]
5048

5149

5250
class ParsedRequirement(NamedTuple):
5351
name: str
5452
url: str
55-
extras: List[str]
53+
extras: list[str]
5654
specifier: str
57-
marker: Optional[MarkerList]
55+
marker: MarkerList | None
5856

5957

6058
# --------------------------------------------------------------------------------------
@@ -87,7 +85,7 @@ def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement:
8785

8886
def _parse_requirement_details(
8987
tokenizer: Tokenizer,
90-
) -> Tuple[str, str, Optional[MarkerList]]:
88+
) -> tuple[str, str, MarkerList | None]:
9189
"""
9290
requirement_details = AT URL (WS requirement_marker?)?
9391
| specifier WS? (requirement_marker)?
@@ -156,7 +154,7 @@ def _parse_requirement_marker(
156154
return marker
157155

158156

159-
def _parse_extras(tokenizer: Tokenizer) -> List[str]:
157+
def _parse_extras(tokenizer: Tokenizer) -> list[str]:
160158
"""
161159
extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)?
162160
"""
@@ -175,11 +173,11 @@ def _parse_extras(tokenizer: Tokenizer) -> List[str]:
175173
return extras
176174

177175

178-
def _parse_extras_list(tokenizer: Tokenizer) -> List[str]:
176+
def _parse_extras_list(tokenizer: Tokenizer) -> list[str]:
179177
"""
180178
extras_list = identifier (wsp* ',' wsp* identifier)*
181179
"""
182-
extras: List[str] = []
180+
extras: list[str] = []
183181

184182
if not tokenizer.check("IDENTIFIER"):
185183
return extras

src/packaging/_tokenizer.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
from __future__ import annotations
2+
13
import contextlib
24
import re
35
from dataclasses import dataclass
4-
from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union
6+
from typing import Iterator, NoReturn
57

68
from .specifiers import Specifier
79

@@ -21,7 +23,7 @@ def __init__(
2123
message: str,
2224
*,
2325
source: str,
24-
span: Tuple[int, int],
26+
span: tuple[int, int],
2527
) -> None:
2628
self.span = span
2729
self.message = message
@@ -34,7 +36,7 @@ def __str__(self) -> str:
3436
return "\n ".join([self.message, self.source, marker])
3537

3638

37-
DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = {
39+
DEFAULT_RULES: dict[str, str | re.Pattern[str]] = {
3840
"LEFT_PARENTHESIS": r"\(",
3941
"RIGHT_PARENTHESIS": r"\)",
4042
"LEFT_BRACKET": r"\[",
@@ -96,13 +98,13 @@ def __init__(
9698
self,
9799
source: str,
98100
*,
99-
rules: "Dict[str, Union[str, re.Pattern[str]]]",
101+
rules: dict[str, str | re.Pattern[str]],
100102
) -> None:
101103
self.source = source
102-
self.rules: Dict[str, re.Pattern[str]] = {
104+
self.rules: dict[str, re.Pattern[str]] = {
103105
name: re.compile(pattern) for name, pattern in rules.items()
104106
}
105-
self.next_token: Optional[Token] = None
107+
self.next_token: Token | None = None
106108
self.position = 0
107109

108110
def consume(self, name: str) -> None:
@@ -154,8 +156,8 @@ def raise_syntax_error(
154156
self,
155157
message: str,
156158
*,
157-
span_start: Optional[int] = None,
158-
span_end: Optional[int] = None,
159+
span_start: int | None = None,
160+
span_end: int | None = None,
159161
) -> NoReturn:
160162
"""Raise ParserSyntaxError at the given position."""
161163
span = (

src/packaging/markers.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@
22
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
33
# for complete details.
44

5+
from __future__ import annotations
6+
57
import operator
68
import os
79
import platform
810
import sys
9-
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
10-
11-
from ._parser import (
12-
MarkerAtom,
13-
MarkerList,
14-
Op,
15-
Value,
16-
Variable,
17-
)
18-
from ._parser import (
19-
parse_marker as _parse_marker,
20-
)
11+
from typing import Any, Callable
12+
13+
from ._parser import MarkerAtom, MarkerList, Op, Value, Variable
14+
from ._parser import parse_marker as _parse_marker
2115
from ._tokenizer import ParserSyntaxError
2216
from .specifiers import InvalidSpecifier, Specifier
2317
from .utils import canonicalize_name
@@ -69,7 +63,7 @@ def _normalize_extra_values(results: Any) -> Any:
6963

7064

7165
def _format_marker(
72-
marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True
66+
marker: list[str] | MarkerAtom | str, first: bool | None = True
7367
) -> str:
7468
assert isinstance(marker, (list, tuple, str))
7569

@@ -96,7 +90,7 @@ def _format_marker(
9690
return marker
9791

9892

99-
_operators: Dict[str, Operator] = {
93+
_operators: dict[str, Operator] = {
10094
"in": lambda lhs, rhs: lhs in rhs,
10195
"not in": lambda lhs, rhs: lhs not in rhs,
10296
"<": operator.lt,
@@ -116,14 +110,14 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
116110
else:
117111
return spec.contains(lhs, prereleases=True)
118112

119-
oper: Optional[Operator] = _operators.get(op.serialize())
113+
oper: Operator | None = _operators.get(op.serialize())
120114
if oper is None:
121115
raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
122116

123117
return oper(lhs, rhs)
124118

125119

126-
def _normalize(*values: str, key: str) -> Tuple[str, ...]:
120+
def _normalize(*values: str, key: str) -> tuple[str, ...]:
127121
# PEP 685 – Comparison of extra names for optional distribution dependencies
128122
# https://peps.python.org/pep-0685/
129123
# > When comparing extra names, tools MUST normalize the names being
@@ -135,8 +129,8 @@ def _normalize(*values: str, key: str) -> Tuple[str, ...]:
135129
return values
136130

137131

138-
def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
139-
groups: List[List[bool]] = [[]]
132+
def _evaluate_markers(markers: MarkerList, environment: dict[str, str]) -> bool:
133+
groups: list[list[bool]] = [[]]
140134

141135
for marker in markers:
142136
assert isinstance(marker, (list, tuple, str))
@@ -165,15 +159,15 @@ def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:
165159
return any(all(item) for item in groups)
166160

167161

168-
def format_full_version(info: "sys._version_info") -> str:
162+
def format_full_version(info: sys._version_info) -> str:
169163
version = "{0.major}.{0.minor}.{0.micro}".format(info)
170164
kind = info.releaselevel
171165
if kind != "final":
172166
version += kind[0] + str(info.serial)
173167
return version
174168

175169

176-
def default_environment() -> Dict[str, str]:
170+
def default_environment() -> dict[str, str]:
177171
iver = format_full_version(sys.implementation.version)
178172
implementation_name = sys.implementation.name
179173
return {
@@ -232,7 +226,7 @@ def __eq__(self, other: Any) -> bool:
232226

233227
return str(self) == str(other)
234228

235-
def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
229+
def evaluate(self, environment: dict[str, str] | None = None) -> bool:
236230
"""Evaluate a marker.
237231
238232
Return the boolean from evaluating the given marker against the

0 commit comments

Comments
 (0)