Skip to content

Commit b53c5d1

Browse files
authored
Drop support for Python 3.8 (#242)
1 parent 22619e6 commit b53c5d1

File tree

11 files changed

+84
-68
lines changed

11 files changed

+84
-68
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
strategy:
2222
matrix:
2323
os: [Windows, Ubuntu, MacOS]
24-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
24+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
2525
include:
2626
# Only run PyPy jobs, on Ubuntu.
2727
- os: Ubuntu

noxfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def lint(session):
2323
session.run("pre-commit", "run", "--all-files", *args)
2424

2525

26-
@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3"])
26+
@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13", "pypy3"])
2727
def test(session):
2828
session.install(".")
2929
session.install("-r", "tests/requirements.txt")
@@ -43,7 +43,7 @@ def test(session):
4343
)
4444

4545

46-
@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3"])
46+
@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13", "pypy3"])
4747
def doctest(session):
4848
session.install(".")
4949
session.install("-r", "docs/requirements.txt")

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ readme = "README.md"
1010
authors = [
1111
{ name = "Pradyun Gedam", email = "[email protected]" },
1212
]
13-
requires-python = ">=3.8"
13+
requires-python = ">=3.9"
1414
classifiers = [
1515
"License :: OSI Approved :: MIT License",
1616
"Programming Language :: Python :: 3 :: Only",
17-
"Programming Language :: Python :: 3.8",
1817
"Programming Language :: Python :: 3.9",
1918
"Programming Language :: Python :: 3.10",
2019
"Programming Language :: Python :: 3.11",
2120
"Programming Language :: Python :: 3.12",
21+
"Programming Language :: Python :: 3.13",
2222
]
2323
dynamic = [
2424
"description",

src/installer/__main__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import os.path
55
import sys
66
import sysconfig
7-
from typing import Dict, Optional, Sequence
7+
from collections.abc import Sequence
8+
from typing import Optional
89

910
import installer
1011
from installer.destinations import SchemeDictionaryDestination
@@ -61,7 +62,7 @@ def _get_main_parser() -> argparse.ArgumentParser:
6162

6263
def _get_scheme_dict(
6364
distribution_name: str, prefix: Optional[str] = None
64-
) -> Dict[str, str]:
65+
) -> dict[str, str]:
6566
"""Calculate the scheme dictionary for the current Python environment."""
6667
vars = {}
6768
if prefix is None:

src/installer/_core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import posixpath
44
from io import BytesIO
5-
from typing import Dict, Tuple, cast
5+
from typing import cast
66

77
from installer.destinations import WheelDestination
88
from installer.exceptions import InvalidWheelSource
@@ -35,7 +35,7 @@ def _process_WHEEL_file(source: WheelSource) -> Scheme: # noqa: N802
3535

3636
def _determine_scheme(
3737
path: str, source: WheelSource, root_scheme: Scheme
38-
) -> Tuple[Scheme, str]:
38+
) -> tuple[Scheme, str]:
3939
"""Determine which scheme to place given path in, from source."""
4040
data_dir = source.data_dir
4141

@@ -64,7 +64,7 @@ def _determine_scheme(
6464
def install(
6565
source: WheelSource,
6666
destination: WheelDestination,
67-
additional_metadata: Dict[str, bytes],
67+
additional_metadata: dict[str, bytes],
6868
) -> None:
6969
"""Install wheel described by ``source`` into ``destination``.
7070

src/installer/destinations.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22

33
import io
44
import os
5+
from collections.abc import Collection, Iterable
56
from dataclasses import dataclass
67
from pathlib import Path
78
from typing import (
89
TYPE_CHECKING,
910
BinaryIO,
10-
Collection,
11-
Dict,
12-
Iterable,
1311
Optional,
14-
Tuple,
1512
Union,
1613
)
1714

@@ -83,7 +80,7 @@ def finalize_installation(
8380
self,
8481
scheme: Scheme,
8582
record_file_path: str,
86-
records: Iterable[Tuple[Scheme, RecordEntry]],
83+
records: Iterable[tuple[Scheme, RecordEntry]],
8784
) -> None:
8885
"""Finalize installation, after all the files are written.
8986
@@ -105,7 +102,7 @@ def finalize_installation(
105102
class SchemeDictionaryDestination(WheelDestination):
106103
"""Destination, based on a mapping of {scheme: file-system-path}."""
107104

108-
scheme_dict: Dict[str, str]
105+
scheme_dict: dict[str, str]
109106
"""A mapping of {scheme: file-system-path}"""
110107

111108
interpreter: str
@@ -258,7 +255,7 @@ def finalize_installation(
258255
self,
259256
scheme: Scheme,
260257
record_file_path: str,
261-
records: Iterable[Tuple[Scheme, RecordEntry]],
258+
records: Iterable[tuple[Scheme, RecordEntry]],
262259
) -> None:
263260
"""Finalize installation, by writing the ``RECORD`` file & compiling bytecode.
264261

src/installer/records.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import csv
55
import hashlib
66
import os
7+
from collections.abc import Iterable, Iterator
78
from dataclasses import dataclass
89
from pathlib import Path
9-
from typing import BinaryIO, Iterable, Iterator, Optional, Tuple, cast
10+
from typing import BinaryIO, Optional, cast
1011

1112
from installer.utils import copyfileobj_with_hashing, get_stream_length
1213

@@ -92,7 +93,7 @@ class RecordEntry:
9293
size: Optional[int]
9394
"""File's size in bytes."""
9495

95-
def to_row(self, path_prefix: Optional[str] = None) -> Tuple[str, str, str]:
96+
def to_row(self, path_prefix: Optional[str] = None) -> tuple[str, str, str]:
9697
"""Convert this into a 3-element tuple that can be written in a RECORD file.
9798
9899
:param path_prefix: A prefix to attach to the path -- must end in `/`
@@ -213,7 +214,7 @@ def from_elements(cls, path: str, hash_: str, size: str) -> "RecordEntry":
213214
return cls(path=path, hash_=hash_value, size=size_value)
214215

215216

216-
def parse_record_file(rows: Iterable[str]) -> Iterator[Tuple[str, str, str]]:
217+
def parse_record_file(rows: Iterable[str]) -> Iterator[tuple[str, str, str]]:
217218
"""Parse a :pep:`376` RECORD.
218219
219220
Returns an iterable of 3-value tuples, that can be passed to
@@ -230,5 +231,5 @@ def parse_record_file(rows: Iterable[str]) -> Iterator[Tuple[str, str, str]]:
230231
# Convert Windows paths to use / for consistency
231232
elements[0] = elements[0].replace("\\", "/")
232233

233-
value = cast(Tuple[str, str, str], tuple(elements))
234+
value = cast(tuple[str, str, str], tuple(elements))
234235
yield value

src/installer/scripts.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import os
55
import shlex
66
import zipfile
7+
from collections.abc import Mapping
78
from dataclasses import dataclass, field
8-
from importlib.resources import read_binary
9-
from typing import TYPE_CHECKING, Mapping, Optional, Tuple
9+
from importlib.resources import files
10+
from typing import TYPE_CHECKING, Optional
1011

1112
from installer import _scripts
1213

@@ -20,7 +21,7 @@
2021
__all__ = ["InvalidScript", "Script"]
2122

2223

23-
_ALLOWED_LAUNCHERS: Mapping[Tuple["ScriptSection", "LauncherKind"], str] = {
24+
_ALLOWED_LAUNCHERS: Mapping[tuple["ScriptSection", "LauncherKind"], str] = {
2425
("console", "win-ia32"): "t32.exe",
2526
("console", "win-amd64"): "t64.exe",
2627
("console", "win-arm"): "t_arm.exe",
@@ -74,7 +75,7 @@ def _get_launcher_data(self, kind: "LauncherKind") -> Optional[bytes]:
7475
except KeyError:
7576
error = f"{key!r} not in {sorted(_ALLOWED_LAUNCHERS)!r}"
7677
raise InvalidScript(error) from None
77-
return read_binary(_scripts, name)
78+
return (files(_scripts) / name).read_bytes()
7879

7980
def _get_alternate_executable(self, executable: str, kind: "LauncherKind") -> str:
8081
"""Get an alternate executable for the launcher.
@@ -87,7 +88,7 @@ def _get_alternate_executable(self, executable: str, kind: "LauncherKind") -> st
8788
executable = os.path.join(dn, fn) # noqa: PTH118
8889
return executable
8990

90-
def generate(self, executable: str, kind: "LauncherKind") -> Tuple[str, bytes]:
91+
def generate(self, executable: str, kind: "LauncherKind") -> tuple[str, bytes]:
9192
"""Generate a launcher for this script.
9293
9394
:param executable: Path to the executable to invoke.

src/installer/sources.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@
33
import posixpath
44
import stat
55
import zipfile
6+
from collections.abc import Iterator
67
from contextlib import contextmanager
78
from functools import cached_property
89
from pathlib import Path
910
from typing import (
1011
TYPE_CHECKING,
1112
BinaryIO,
1213
ClassVar,
13-
Iterator,
14-
List,
1514
Optional,
16-
Tuple,
17-
Type,
1815
cast,
1916
)
2017

@@ -25,7 +22,7 @@
2522
if TYPE_CHECKING:
2623
import os
2724

28-
WheelContentElement = Tuple[Tuple[str, str, str], BinaryIO, bool]
25+
WheelContentElement = tuple[tuple[str, str, str], BinaryIO, bool]
2926

3027

3128
__all__ = ["WheelSource", "WheelFile"]
@@ -37,7 +34,7 @@ class WheelSource:
3734
This is an abstract class, whose methods have to be implemented by subclasses.
3835
"""
3936

40-
validation_error: ClassVar[Type[Exception]] = ValueError #: :meta hide-value:
37+
validation_error: ClassVar[type[Exception]] = ValueError #: :meta hide-value:
4138
"""
4239
.. versionadded:: 0.7.0
4340
@@ -66,7 +63,7 @@ def data_dir(self) -> str:
6663
return f"{self.distribution}-{self.version}.data"
6764

6865
@property
69-
def dist_info_filenames(self) -> List[str]:
66+
def dist_info_filenames(self) -> list[str]:
7067
"""Get names of all files in the dist-info directory.
7168
7269
Sample usage/behaviour::
@@ -127,7 +124,7 @@ def get_contents(self) -> Iterator[WheelContentElement]:
127124
class _WheelFileValidationError(ValueError, InstallerError):
128125
"""Raised when a wheel file fails validation."""
129126

130-
def __init__(self, issues: List[str]) -> None:
127+
def __init__(self, issues: list[str]) -> None:
131128
super().__init__(repr(issues))
132129
self.issues = issues
133130

@@ -217,7 +214,7 @@ def dist_info_dir(self) -> str:
217214
return dist_info_dir
218215

219216
@property
220-
def dist_info_filenames(self) -> List[str]:
217+
def dist_info_filenames(self) -> list[str]:
221218
"""Get names of all files in the dist-info directory."""
222219
base = self.dist_info_dir
223220
return [
@@ -255,7 +252,7 @@ def validate_record(self, *, validate_contents: bool = True) -> None:
255252
[f"Unable to retrieve `RECORD` from {self._zipfile.filename}: {exc!r}"]
256253
) from exc
257254

258-
issues: List[str] = []
255+
issues: list[str] = []
259256

260257
for item in self._zipfile.infolist():
261258
if item.filename[-1:] == "/": # looks like a directory

src/installer/utils.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import re
1010
import sys
1111
from collections import namedtuple
12+
from collections.abc import Iterable, Iterator
1213
from configparser import ConfigParser
1314
from email.message import Message
1415
from email.parser import FeedParser
@@ -18,11 +19,8 @@
1819
TYPE_CHECKING,
1920
BinaryIO,
2021
Callable,
21-
Iterable,
22-
Iterator,
2322
NewType,
2423
Optional,
25-
Tuple,
2624
cast,
2725
)
2826

@@ -31,7 +29,7 @@
3129
from installer.scripts import LauncherKind, ScriptSection
3230

3331
Scheme = NewType("Scheme", str)
34-
AllSchemes = Tuple[Scheme, ...]
32+
AllSchemes = tuple[Scheme, ...]
3533

3634
__all__ = [
3735
"parse_metadata_file",
@@ -117,7 +115,7 @@ def copyfileobj_with_hashing(
117115
source: BinaryIO,
118116
dest: BinaryIO,
119117
hash_algorithm: str,
120-
) -> Tuple[str, int]:
118+
) -> tuple[str, int]:
121119
"""Copy a buffer while computing the content's hash and size.
122120
123121
Copies the source buffer into the destination buffer while computing the
@@ -206,7 +204,7 @@ def fix_shebang(stream: BinaryIO, interpreter: str) -> Iterator[BinaryIO]:
206204

207205

208206
def construct_record_file(
209-
records: Iterable[Tuple[Scheme, "RecordEntry"]],
207+
records: Iterable[tuple[Scheme, "RecordEntry"]],
210208
prefix_for_scheme: Callable[[Scheme], Optional[str]] = lambda _: None,
211209
) -> BinaryIO:
212210
"""Construct a RECORD file.
@@ -228,7 +226,7 @@ def construct_record_file(
228226
return stream.detach()
229227

230228

231-
def parse_entrypoints(text: str) -> Iterable[Tuple[str, str, str, "ScriptSection"]]:
229+
def parse_entrypoints(text: str) -> Iterable[tuple[str, str, str, "ScriptSection"]]:
232230
"""Parse ``entry_points.txt``-style files.
233231
234232
:param text: entire contents of the file

0 commit comments

Comments
 (0)