Skip to content

Commit 67d0971

Browse files
authored
Release v2025.12.1
2 parents accad06 + d10d38b commit 67d0971

File tree

10 files changed

+289
-208
lines changed

10 files changed

+289
-208
lines changed

.bumper.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tool.bumper]
2-
current_version = "2025.12.0"
2+
current_version = "2025.12.1"
33
versioning_type = "calver"
44

55
[[tool.bumper.files]]

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ci:
33

44
repos:
55
- repo: https://github.com/psf/black-pre-commit-mirror
6-
rev: 25.11.0
6+
rev: 25.12.0
77
hooks:
88
- id: black
99
- repo: https://github.com/pycqa/isort

.pre-commit-hooks.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@
44
language: python
55
files: '^pyproject.toml$'
66
types: [toml]
7+
8+
- id: check-eol-cached
9+
name: Check supported Python EOL (cache only)
10+
entry: checkeol --cache_only
11+
language: python
12+
files: '^pyproject.toml$'
13+
types: [toml]

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog
22
Versions follow [Calendar Versioning](https://calver.org/) (`<YYYY>`.`<MM>`.`<MICRO>`)
33

4+
## [v2025.12.1]
5+
### Added
6+
* #8 Add the `check-eol-cached` hook, which utilizies only the cached EOL information and does not incorporate a date-based check
7+
48
## [v2025.12.0]
59
### Fixed
610
* #6 Fix EOL cache file not being included in source distribution

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,25 @@ Add this to your `.pre-commit-config.yaml`
1111

1212
```yaml
1313
- repo: https://github.com/sco1/pre-commit-python-eol
14-
rev: v2025.12.0
14+
rev: v2025.12.1
1515
hooks:
1616
- id: check-eol
17+
- id: check-eol-cached
1718
```
1819
20+
While both hooks are technically compatible with each other, it's advised to choose a single hook behavior that best fits your needs.
21+
22+
### EOL Status Cache
23+
To avoid requiring network connectivity at runtime, EOL status is cached to [a local JSON file](./pre_commit_python_eol/cached_release_cycle.json) distributed alongside this hook. The cache is updated quarterly & a changed cache will result in a version bump for this hook.
24+
1925
## Hooks
20-
**NOTE:** Only pyproject.toml is currently inspected. It is assumed that project metadata is specified per [PyPA Guidance](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/)
26+
Only `pyproject.toml` is currently inspected. It is assumed that project metadata is specified per [PyPA Guidance](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/)
2127

2228
### `check-eol`
23-
Check `requires-python` against the current Python lifecycle & fail if an EOL version is included.
29+
Check `requires-python` against the current Python lifecycle & fail if an EOL version is included; this includes a date-based check using the system's time for versions that have not yet explicitly been declared EOL.
30+
31+
### `check-eol-cached`
32+
Check `requires-python` against the current Python lifecycle & fail if an EOL version is included; this hook utilizes only the cached release cycle information.
2433

2534
## Python Version Support
2635
Starting with Python 3.11, a best attempt is made to support Python versions until they reach EOL, after which support will be formally dropped by the next minor or major release of this package, whichever arrives first. The status of Python versions can be found [here](https://devguide.python.org/versions/).

pre_commit_python_eol/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "2025.12.0"
1+
__version__ = "2025.12.1"
22
__url__ = "https://github.com/sco1/pre-commit-check-eol"

pre_commit_python_eol/check_eol.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,23 @@ def from_json(cls, ver: str, metadata: dict[str, t.Any]) -> PythonRelease:
8181
end_of_life=_parse_eol_date(metadata["end_of_life"]),
8282
)
8383

84+
def is_eol(self, use_system_date: bool) -> bool:
85+
"""
86+
Check if this version is end-of-life.
87+
88+
If `use_system_date` is `True`, an additional date-based check is performed for versions
89+
that are not explicitly EOL.
90+
"""
91+
if self.status == ReleasePhase.EOL:
92+
return True
93+
94+
if use_system_date:
95+
utc_today = dt.datetime.now(dt.timezone.utc).date()
96+
if self.end_of_life <= utc_today:
97+
return True
98+
99+
return False
100+
84101

85102
def _get_cached_release_cycle(cache_json: Path) -> list[PythonRelease]:
86103
"""
@@ -100,12 +117,17 @@ def _get_cached_release_cycle(cache_json: Path) -> list[PythonRelease]:
100117
)
101118

102119

103-
def check_python_support(toml_file: Path, cache_json: Path = CACHED_RELEASE_CYCLE) -> None:
120+
def check_python_support(
121+
toml_file: Path, cache_json: Path = CACHED_RELEASE_CYCLE, use_system_date: bool = True
122+
) -> None:
104123
"""
105124
Check the input TOML's `requires-python` for overlap with EOL Python version(s).
106125
107126
If overlap(s) are present, an exception is raised whose message enumerates all EOL Python
108127
versions supported by the TOML file.
128+
129+
If `use_system_date` is `True`, an additional date-based check is performed for versions that
130+
are not explicitly EOL.
109131
"""
110132
with toml_file.open("rb") as f:
111133
contents = tomllib.load(f)
@@ -116,18 +138,10 @@ def check_python_support(toml_file: Path, cache_json: Path = CACHED_RELEASE_CYCL
116138

117139
package_spec = specifiers.SpecifierSet(requires_python)
118140
release_cycle = _get_cached_release_cycle(cache_json)
119-
utc_today = dt.datetime.now(dt.timezone.utc).date()
120-
121-
eol_supported = []
122-
for r in release_cycle:
123-
if r.python_ver in package_spec:
124-
if r.status == ReleasePhase.EOL:
125-
eol_supported.append(r)
126-
continue
127141

128-
if r.end_of_life <= utc_today:
129-
eol_supported.append(r)
130-
continue
142+
eol_supported = [
143+
r for r in release_cycle if ((r.python_ver in package_spec) and r.is_eol(use_system_date))
144+
]
131145

132146
if eol_supported:
133147
eol_supported.sort(key=attrgetter("python_ver")) # Sort ascending for error msg generation
@@ -138,12 +152,13 @@ def check_python_support(toml_file: Path, cache_json: Path = CACHED_RELEASE_CYCL
138152
def main(argv: abc.Sequence[str] | None = None) -> int: # noqa: D103
139153
parser = argparse.ArgumentParser()
140154
parser.add_argument("filenames", nargs="*", type=Path)
155+
parser.add_argument("--cache_only", action="store_true")
141156
args = parser.parse_args(argv)
142157

143158
ec = 0
144159
for file in args.filenames:
145160
try:
146-
check_python_support(file)
161+
check_python_support(file, use_system_date=(not args.cache_only))
147162
except EOLPythonError as e:
148163
print(f"{file}: {e}")
149164
ec = 1

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pre-commit-python-eol"
3-
version = "2025.12.0"
3+
version = "2025.12.1"
44
description = "A pre-commit hook for enforcing supported Python EOL"
55
license = "MIT"
66
license-files = ["LICENSE"]

tests/test_check_eol.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,45 @@ def test_check_python_support_multi_eol_raises(path_with_cache: tuple[Path, Path
213213
check_python_support(pyproject, cache_json=cache_path)
214214

215215
assert str(e.value).endswith("3.7, 3.8")
216+
217+
218+
def test_check_cached_python_support_single_eol_no_raises_by_date(
219+
path_with_cache: tuple[Path, Path],
220+
) -> None:
221+
base_path, cache_path = path_with_cache
222+
pyproject = base_path / "pyproject.toml"
223+
pyproject.write_text(SAMPLE_PYPROJECT_SINGLE_EOL_BY_DATE)
224+
225+
with time_machine.travel(dt.date(year=2031, month=11, day=1)):
226+
check_python_support(
227+
pyproject,
228+
cache_json=cache_path,
229+
use_system_date=False,
230+
)
231+
232+
233+
def test_check_cached_python_support_no_eol(path_with_cache: tuple[Path, Path]) -> None:
234+
base_path, cache_path = path_with_cache
235+
pyproject = base_path / "pyproject.toml"
236+
pyproject.write_text(SAMPLE_PYPROJECT_NO_EOL)
237+
238+
check_python_support(
239+
pyproject,
240+
cache_json=cache_path,
241+
use_system_date=False,
242+
)
243+
244+
245+
def test_check_cached_python_support_single_eol_raises(path_with_cache: tuple[Path, Path]) -> None:
246+
base_path, cache_path = path_with_cache
247+
pyproject = base_path / "pyproject.toml"
248+
pyproject.write_text(SAMPLE_PYPROJECT_SINGLE_EOL)
249+
250+
with pytest.raises(EOLPythonError) as e:
251+
check_python_support(
252+
pyproject,
253+
cache_json=cache_path,
254+
use_system_date=False,
255+
)
256+
257+
assert str(e.value).endswith("3.8")

0 commit comments

Comments
 (0)