Skip to content

Commit 558fc35

Browse files
authored
Merge pull request #261 from python-grimp/drop-py39
Drop py39
2 parents 045c175 + 06a831d commit 558fc35

29 files changed

+122
-127
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
strategy:
4040
matrix:
4141
python-version: [
42-
"3.9", "3.10", "3.11", "3.12", "3.13", "3.14"
42+
"3.10", "3.11", "3.12", "3.13", "3.14"
4343
]
4444
os: [ubuntu-latest, macos-latest, windows-latest]
4545

.github/workflows/release.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ jobs:
3737
- uses: actions/setup-python@v6
3838
with:
3939
python-version: |
40-
3.9
4140
3.10
4241
3.11
4342
3.12
@@ -75,7 +74,6 @@ jobs:
7574
- uses: actions/setup-python@v6
7675
with:
7776
python-version: |
78-
3.9
7977
3.10
8078
3.11
8179
3.12
@@ -109,7 +107,6 @@ jobs:
109107
- uses: actions/setup-python@v6
110108
with:
111109
python-version: |
112-
3.9
113110
3.10
114111
3.11
115112
3.12
@@ -143,7 +140,6 @@ jobs:
143140
- uses: actions/setup-python@v6
144141
with:
145142
python-version: |
146-
3.9
147143
3.10
148144
3.11
149145
3.12

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog
33
=========
44

5+
latest
6+
------
7+
8+
* Drop support for Python 3.9.
9+
510
3.13 (2025-10-29)
611
-----------------
712

docs/conf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# -*- coding: utf-8 -*-
21
#
32
# Configuration file for the Sphinx documentation builder.
43
#

justfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ lint-rust:
114114
autofix-rust:
115115
@cargo clippy --all-targets --all-features --fix --allow-staged --allow-dirty
116116

117+
# Fix any ruff errors
118+
[group('linting')]
119+
autofix-python:
120+
@uv run ruff check --fix
121+
117122
# Run linters.
118123
[group('linting')]
119124
lint:
@@ -151,6 +156,13 @@ show-benchmark-results:
151156
benchmark-ci:
152157
@uv run --group=benchmark-ci pytest --codspeed
153158

159+
# Upgrade Python code to the supplied version. (E.g. just upgrade 310)
160+
[group('maintenance')]
161+
upgrade-python MIN_VERSION:
162+
@find {docs,src,tests} -name "*.py" -not -path "tests/assets/*" -exec uv run pyupgrade --py{{MIN_VERSION}}-plus --exit-zero-even-if-changed {} +
163+
@just autofix-python
164+
@just format-python
165+
154166
# Run all linters, build docs and tests. Worth running before pushing to Github.
155167
[group('prepush')]
156168
full-check:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ description = "Builds a queryable graph of the imports within one or more Python
1414
authors = [
1515
{name = "David Seddon", email = "[email protected]"},
1616
]
17-
requires-python = ">=3.9"
17+
requires-python = ">=3.10"
1818
dependencies = [
1919
"typing-extensions>=3.10.0.0",
2020
]
@@ -26,7 +26,6 @@ classifiers = [
2626
"Operating System :: POSIX",
2727
"Operating System :: Microsoft :: Windows",
2828
"Programming Language :: Python",
29-
"Programming Language :: Python :: 3.9",
3029
"Programming Language :: Python :: 3.10",
3130
"Programming Language :: Python :: 3.11",
3231
"Programming Language :: Python :: 3.12",
@@ -61,6 +60,7 @@ dev = [
6160
"requests==2.32.3",
6261
"sqlalchemy==2.0.35",
6362
"google-cloud-audit-log==0.3.0",
63+
"pyupgrade>=3.21.0",
6464
]
6565
docs = [
6666
"sphinx>=7.4.7",

src/grimp/adaptors/caching.py

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

33
import json
44
import logging
5-
from typing import Dict, List, Optional, Set, Tuple, Type
5+
from typing import Optional
66

77
from grimp.application.ports.filesystem import AbstractFileSystem
88
from grimp.application.ports.modulefinder import FoundPackage, ModuleFile
@@ -13,7 +13,7 @@
1313
from grimp import _rustgrimp as rust # type: ignore[attr-defined]
1414

1515
logger = logging.getLogger(__name__)
16-
PrimitiveFormat = Dict[str, List[Tuple[str, Optional[int], str]]]
16+
PrimitiveFormat = dict[str, list[tuple[str, Optional[int], str]]]
1717

1818

1919
class CacheFileNamer:
@@ -24,7 +24,7 @@ def make_meta_file_name(cls, found_package: FoundPackage) -> str:
2424
@classmethod
2525
def make_data_file_name(
2626
cls,
27-
found_packages: Set[FoundPackage],
27+
found_packages: set[FoundPackage],
2828
include_external_packages: bool,
2929
exclude_type_checking_imports: bool,
3030
) -> str:
@@ -42,7 +42,7 @@ def make_data_file_name(
4242
@classmethod
4343
def make_data_file_unique_string(
4444
cls,
45-
found_packages: Set[FoundPackage],
45+
found_packages: set[FoundPackage],
4646
include_external_packages: bool,
4747
exclude_type_checking_imports: bool,
4848
) -> str:
@@ -65,24 +65,24 @@ def make_data_file_unique_string(
6565
class Cache(AbstractCache):
6666
DEFAULT_CACHE_DIR = ".grimp_cache"
6767

68-
def __init__(self, *args, namer: Type[CacheFileNamer], **kwargs) -> None:
68+
def __init__(self, *args, namer: type[CacheFileNamer], **kwargs) -> None:
6969
"""
7070
Don't instantiate Cache directly; use Cache.setup().
7171
"""
7272
super().__init__(*args, **kwargs)
73-
self._mtime_map: Dict[str, float] = {}
74-
self._data_map: Dict[Module, Set[DirectImport]] = {}
73+
self._mtime_map: dict[str, float] = {}
74+
self._data_map: dict[Module, set[DirectImport]] = {}
7575
self._namer = namer
7676

7777
@classmethod
7878
def setup(
7979
cls,
8080
file_system: AbstractFileSystem,
81-
found_packages: Set[FoundPackage],
81+
found_packages: set[FoundPackage],
8282
include_external_packages: bool,
8383
exclude_type_checking_imports: bool = False,
84-
cache_dir: Optional[str] = None,
85-
namer: Type[CacheFileNamer] = CacheFileNamer,
84+
cache_dir: str | None = None,
85+
namer: type[CacheFileNamer] = CacheFileNamer,
8686
) -> "Cache":
8787
cache = cls(
8888
file_system=file_system,
@@ -98,10 +98,10 @@ def setup(
9898
return cache
9999

100100
@classmethod
101-
def cache_dir_or_default(cls, cache_dir: Optional[str]) -> str:
101+
def cache_dir_or_default(cls, cache_dir: str | None) -> str:
102102
return cache_dir or cls.DEFAULT_CACHE_DIR
103103

104-
def read_imports(self, module_file: ModuleFile) -> Set[DirectImport]:
104+
def read_imports(self, module_file: ModuleFile) -> set[DirectImport]:
105105
try:
106106
cached_mtime = self._mtime_map[module_file.module.name]
107107
except KeyError:
@@ -118,7 +118,7 @@ def read_imports(self, module_file: ModuleFile) -> Set[DirectImport]:
118118

119119
def write(
120120
self,
121-
imports_by_module: Dict[Module, Set[DirectImport]],
121+
imports_by_module: dict[Module, set[DirectImport]],
122122
) -> None:
123123
self._write_marker_files_if_not_already_there()
124124
# Write data file.
@@ -165,13 +165,13 @@ def write(
165165
def _build_mtime_map(self) -> None:
166166
self._mtime_map = self._read_mtime_map_files()
167167

168-
def _read_mtime_map_files(self) -> Dict[str, float]:
169-
all_mtimes: Dict[str, float] = {}
168+
def _read_mtime_map_files(self) -> dict[str, float]:
169+
all_mtimes: dict[str, float] = {}
170170
for found_package in self.found_packages:
171171
all_mtimes.update(self._read_mtime_map_file(found_package))
172172
return all_mtimes
173173

174-
def _read_mtime_map_file(self, found_package: FoundPackage) -> Dict[str, float]:
174+
def _read_mtime_map_file(self, found_package: FoundPackage) -> dict[str, float]:
175175
meta_cache_filename = self.file_system.join(
176176
self.cache_dir, self._namer.make_meta_file_name(found_package)
177177
)
@@ -191,7 +191,7 @@ def _read_mtime_map_file(self, found_package: FoundPackage) -> Dict[str, float]:
191191
def _build_data_map(self) -> None:
192192
self._data_map = self._read_data_map_file()
193193

194-
def _read_data_map_file(self) -> Dict[Module, Set[DirectImport]]:
194+
def _read_data_map_file(self) -> dict[Module, set[DirectImport]]:
195195
data_cache_filename = self.file_system.join(
196196
self.cache_dir,
197197
self._namer.make_data_file_name(

src/grimp/adaptors/filesystem.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
import tokenize
3-
from typing import Iterator, List, Tuple
3+
from collections.abc import Iterator
44

55
from grimp.application.ports.filesystem import AbstractFileSystem, BasicFileSystem
66
from grimp import _rustgrimp as rust # type: ignore[attr-defined]
@@ -18,13 +18,13 @@ def sep(self) -> str:
1818
def dirname(self, filename: str) -> str:
1919
return os.path.dirname(filename)
2020

21-
def walk(self, directory_name: str) -> Iterator[Tuple[str, List[str], List[str]]]:
21+
def walk(self, directory_name: str) -> Iterator[tuple[str, list[str], list[str]]]:
2222
yield from os.walk(directory_name, followlinks=True)
2323

2424
def join(self, *components: str) -> str:
2525
return os.path.join(*components)
2626

27-
def split(self, file_name: str) -> Tuple[str, str]:
27+
def split(self, file_name: str) -> tuple[str, str]:
2828
return os.path.split(file_name)
2929

3030
def read(self, file_name: str) -> str:

src/grimp/adaptors/modulefinder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Iterable, List
2+
from collections.abc import Iterable
33

44
from grimp.application.ports import modulefinder
55
from grimp.application.ports.filesystem import AbstractFileSystem
@@ -14,7 +14,7 @@ def find_package(
1414
) -> modulefinder.FoundPackage:
1515
self.file_system = file_system
1616

17-
module_files: List[modulefinder.ModuleFile] = []
17+
module_files: list[modulefinder.ModuleFile] = []
1818

1919
for module_filename in self._get_python_files_inside_package(package_directory):
2020
module_name = self._module_name_from_filename(

src/grimp/adaptors/packagefinder.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@ def determine_package_directory(
1919
# Attempt to locate the package file.
2020
spec = importlib.util.find_spec(package_name)
2121
if not spec:
22-
logger.debug("sys.path: {}".format(sys.path))
23-
raise ValueError(
24-
"Could not find package '{}' in your Python path.".format(package_name)
25-
)
22+
logger.debug(f"sys.path: {sys.path}")
23+
raise ValueError(f"Could not find package '{package_name}' in your Python path.")
2624

2725
if spec.has_location and spec.origin:
2826
if not self._is_a_package(spec, file_system) or self._has_a_non_namespace_parent(spec):

0 commit comments

Comments
 (0)