Skip to content

Commit bd13fa0

Browse files
committed
Release v2025.5.0
1 parent c95a2d6 commit bd13fa0

File tree

12 files changed

+379
-71
lines changed

12 files changed

+379
-71
lines changed

.bumper.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[tool.bumper]
2-
current_version = "0.1.0"
2+
current_version = "2025.5.0"
3+
versioning_type = "calver"
34

45
[[tool.bumper.files]]
56
file = "./pyproject.toml"

.github/workflows/pypi_release.yml renamed to .github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ jobs:
3333
env:
3434
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3535
run:
36-
gh release upload ${{ github.event.release.tag_name }} ./dist/pre_commit_python_eol-*.whl
36+
gh release upload ${{ github.event.release.tag_name }} ./dist/pre_commit_python_eol-*.whl

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
# Changelog
2-
Versions follow [Calendar Versioning](https://calver.org/) (`<YYYY>`.`<0M>`.`<MICRO>`)
2+
Versions follow [Calendar Versioning](https://calver.org/) (`<YYYY>`.`<MM>`.`<MICRO>`)
3+
4+
## [v2025.5.0]
5+
Initial release - yay!

README.md

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

1212
```yaml
1313
- repo: https://github.com/sco1/pre-commit-python-eol
14-
rev: v0.1.0
14+
rev: v2025.5.0
1515
hooks:
1616
- id: check-eol
1717
```
1818
1919
## Hooks
20-
**NOTE:** Only pyproject.toml is currently inspected. It is assumed that project metadata is supported per [PyPA Guidance](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/)
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/)
2121
2222
### `check-eol`
2323
Check `requires-python` against the current Python lifecycle & fail if an EOL version is included.

cached_release_cycle.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
{
2-
"3.14": {
2+
"3.15": {
33
"branch": "main",
4-
"pep": 745,
4+
"pep": 790,
55
"status": "feature",
6-
"first_release": "2025-10-01",
6+
"first_release": "2026-10-01",
7+
"end_of_life": "2031-10",
8+
"release_manager": "Hugo van Kemenade"
9+
},
10+
"3.14": {
11+
"branch": "3.14",
12+
"pep": 745,
13+
"status": "prerelease",
14+
"first_release": "2025-10-07",
715
"end_of_life": "2030-10",
816
"release_manager": "Hugo van Kemenade"
917
},

pre_commit_python_eol/check_eol.py

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from collections import abc
1010
from dataclasses import dataclass
1111
from enum import StrEnum
12+
from operator import attrgetter
1213
from pathlib import Path
1314

1415
from packaging import specifiers, version
@@ -58,25 +59,12 @@ def _parse_eol_date(date_str: str) -> dt.date:
5859

5960

6061
@dataclass(frozen=True)
61-
class PythonRelease:
62-
"""
63-
Represent the relevant metadata information for a Python release.
64-
65-
For the purposes of this tool, instances of `PythonRelease` are considered equal if their
66-
respective `python_ver` attributes are equal.
67-
"""
68-
62+
class PythonRelease: # noqa: D101
6963
python_ver: version.Version
7064
status: ReleasePhase
7165
end_of_life: dt.date
7266

73-
def __eq__(self, other: object) -> bool:
74-
if not isinstance(other, PythonRelease):
75-
return NotImplemented
76-
77-
return self.python_ver == other.python_ver
78-
79-
def __str__(self) -> str:
67+
def __str__(self) -> str: # pragma: no cover
8068
return f"Python {self.python_ver} - Status: {self.status}, EOL: {self.end_of_life}"
8169

8270
@classmethod
@@ -94,15 +82,25 @@ def from_json(cls, ver: str, metadata: dict[str, t.Any]) -> PythonRelease:
9482
)
9583

9684

97-
def _get_cached_release_cycle(cache_json: Path = CACHED_RELEASE_CYCLE) -> set[PythonRelease]:
98-
"""Parse the locally cached Python release cycle into `PythonRelease` instance(s)."""
85+
def _get_cached_release_cycle(cache_json: Path) -> list[PythonRelease]:
86+
"""
87+
Parse the locally cached Python release cycle into `PythonRelease` instance(s).
88+
89+
Results are sorted by Python version in descending order.
90+
"""
9991
with cache_json.open("r", encoding="utf-8") as f:
10092
contents = json.load(f)
10193

102-
return {PythonRelease.from_json(v, m) for v, m in contents.items()}
94+
# The sorting is probably unnecessary since the JSON should already be sorted, but going to
95+
# retain since it's expected downstream
96+
return sorted(
97+
(PythonRelease.from_json(v, m) for v, m in contents.items()),
98+
key=attrgetter("python_ver"),
99+
reverse=True,
100+
)
103101

104102

105-
def check_python_support(toml_file: Path) -> None:
103+
def check_python_support(toml_file: Path, cache_json: Path = CACHED_RELEASE_CYCLE) -> None:
106104
with toml_file.open("rb") as f:
107105
contents = tomllib.load(f)
108106

@@ -111,8 +109,24 @@ def check_python_support(toml_file: Path) -> None:
111109
raise RequiresPythonNotFoundError
112110

113111
package_spec = specifiers.SpecifierSet(requires_python)
112+
release_cycle = _get_cached_release_cycle(cache_json)
113+
utc_today = dt.datetime.now(dt.timezone.utc).date()
114+
115+
eol_supported = []
116+
for r in release_cycle:
117+
if r.python_ver in package_spec:
118+
if r.status == ReleasePhase.EOL:
119+
eol_supported.append(r)
120+
continue
121+
122+
if r.end_of_life <= utc_today:
123+
eol_supported.append(r)
124+
continue
114125

115-
raise NotImplementedError
126+
if eol_supported:
127+
eol_supported.sort(key=attrgetter("python_ver")) # Sort ascending for error msg generation
128+
joined_vers = ", ".join(str(r.python_ver) for r in eol_supported)
129+
raise EOLPythonError(f"EOL Python support found: {joined_vers}")
116130

117131

118132
def main(argv: abc.Sequence[str] | None = None) -> int: # noqa: D103
@@ -124,8 +138,8 @@ def main(argv: abc.Sequence[str] | None = None) -> int: # noqa: D103
124138
for file in args.filenames:
125139
try:
126140
check_python_support(file)
127-
except EOLPythonError:
128-
print(f"{file}: Fail.")
141+
except EOLPythonError as e:
142+
print(f"{file}: {e}")
129143
ec = 1
130144
except RequiresPythonNotFoundError:
131145
print(f"{file} 'requires-python' could not be located, or it is empty.")

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pre-commit-python-eol"
3-
version = "0.1.0"
3+
version = "2025.5.0"
44
description = "A pre-commit hook for enforcing supported Python EOL"
55
license = "MIT"
66
license-files = ["LICENSE"]
@@ -22,7 +22,7 @@ classifiers = [
2222

2323
requires-python = ">=3.11"
2424
dependencies = [
25-
"packaging~=24.2",
25+
"packaging~=25.0",
2626
]
2727

2828
[project.urls]
@@ -47,7 +47,8 @@ dev-dependencies = [
4747
"pytest-cov~=6.0",
4848
"pytest-randomly~=3.16",
4949
"ruff~=0.9",
50-
"sco1-bumper~=1.0",
50+
"sco1-bumper~=2.0",
51+
"time-machine~=2.16",
5152
"tox~=4.23",
5253
"tox-uv~=1.17",
5354
]

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)