Skip to content

Commit 5cfd94e

Browse files
authored
Convert Version to a dataclass (#279)
1 parent 8840460 commit 5cfd94e

File tree

2 files changed

+46
-55
lines changed

2 files changed

+46
-55
lines changed

build_docs.py

+22-31
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import sys
5959
from bisect import bisect_left as bisect
6060
from contextlib import contextmanager, suppress
61-
from functools import total_ordering
6261
from pathlib import Path
6362
from string import Template
6463
from time import perf_counter, sleep
@@ -103,11 +102,23 @@ def __reversed__(self) -> Iterator[Version]:
103102

104103
@classmethod
105104
def from_json(cls, data: dict) -> Versions:
106-
versions = sorted(
107-
[Version.from_json(name, release) for name, release in data.items()],
108-
key=Version.as_tuple,
109-
)
110-
return cls(versions)
105+
"""Load versions from the devguide's JSON representation."""
106+
permitted = ", ".join(sorted(Version.STATUSES | Version.SYNONYMS.keys()))
107+
108+
versions = []
109+
for name, release in data.items():
110+
branch = release["branch"]
111+
status = release["status"]
112+
status = Version.SYNONYMS.get(status, status)
113+
if status not in Version.STATUSES:
114+
msg = (
115+
f"Saw invalid version status {status!r}, "
116+
f"expected to be one of {permitted}."
117+
)
118+
raise ValueError(msg)
119+
versions.append(Version(name=name, status=status, branch_or_tag=branch))
120+
121+
return cls(sorted(versions, key=Version.as_tuple))
111122

112123
def filter(self, branches: Sequence[str] = ()) -> Sequence[Version]:
113124
"""Filter the given versions.
@@ -143,10 +154,14 @@ def setup_indexsidebar(self, current: Version, dest_path: Path) -> None:
143154
dest_path.write_text(rendered_template, encoding="UTF-8")
144155

145156

146-
@total_ordering
157+
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
147158
class Version:
148159
"""Represents a CPython version and its documentation build dependencies."""
149160

161+
name: str
162+
status: Literal["EOL", "security-fixes", "stable", "pre-release", "in development"]
163+
branch_or_tag: str
164+
150165
STATUSES = {"EOL", "security-fixes", "stable", "pre-release", "in development"}
151166

152167
# Those synonyms map branch status vocabulary found in the devguide
@@ -159,33 +174,9 @@ class Version:
159174
"prerelease": "pre-release",
160175
}
161176

162-
def __init__(
163-
self, name: str, *, status: str, branch_or_tag: str | None = None
164-
) -> None:
165-
status = self.SYNONYMS.get(status, status)
166-
if status not in self.STATUSES:
167-
raise ValueError(
168-
"Version status expected to be one of: "
169-
f"{', '.join(self.STATUSES | set(self.SYNONYMS.keys()))}, got {status!r}."
170-
)
171-
self.name = name
172-
self.branch_or_tag = branch_or_tag
173-
self.status = status
174-
175-
def __repr__(self) -> str:
176-
return f"Version({self.name})"
177-
178177
def __eq__(self, other: Version) -> bool:
179178
return self.name == other.name
180179

181-
def __gt__(self, other: Version) -> bool:
182-
return self.as_tuple() > other.as_tuple()
183-
184-
@classmethod
185-
def from_json(cls, name: str, values: dict) -> Version:
186-
"""Loads a version from devguide's json representation."""
187-
return cls(name, status=values["status"], branch_or_tag=values["branch"])
188-
189180
@property
190181
def requirements(self) -> list[str]:
191182
"""Generate the right requirements for this version.

tests/test_build_docs_versions.py

+24-24
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,59 @@
44
def test_filter_default() -> None:
55
# Arrange
66
versions = Versions([
7-
Version("3.14", status="feature"),
8-
Version("3.13", status="bugfix"),
9-
Version("3.12", status="bugfix"),
10-
Version("3.11", status="security"),
11-
Version("3.10", status="security"),
12-
Version("3.9", status="security"),
7+
Version(name="3.14", status="in development", branch_or_tag=""),
8+
Version(name="3.13", status="stable", branch_or_tag=""),
9+
Version(name="3.12", status="stable", branch_or_tag=""),
10+
Version(name="3.11", status="security-fixes", branch_or_tag=""),
11+
Version(name="3.10", status="security-fixes", branch_or_tag=""),
12+
Version(name="3.9", status="security-fixes", branch_or_tag=""),
1313
])
1414

1515
# Act
1616
filtered = versions.filter()
1717

1818
# Assert
1919
assert filtered == [
20-
Version("3.14", status="feature"),
21-
Version("3.13", status="bugfix"),
22-
Version("3.12", status="bugfix"),
20+
Version(name="3.14", status="in development", branch_or_tag=""),
21+
Version(name="3.13", status="stable", branch_or_tag=""),
22+
Version(name="3.12", status="stable", branch_or_tag=""),
2323
]
2424

2525

2626
def test_filter_one() -> None:
2727
# Arrange
2828
versions = Versions([
29-
Version("3.14", status="feature"),
30-
Version("3.13", status="bugfix"),
31-
Version("3.12", status="bugfix"),
32-
Version("3.11", status="security"),
33-
Version("3.10", status="security"),
34-
Version("3.9", status="security"),
29+
Version(name="3.14", status="in development", branch_or_tag=""),
30+
Version(name="3.13", status="stable", branch_or_tag=""),
31+
Version(name="3.12", status="stable", branch_or_tag=""),
32+
Version(name="3.11", status="security-fixes", branch_or_tag=""),
33+
Version(name="3.10", status="security-fixes", branch_or_tag=""),
34+
Version(name="3.9", status="security-fixes", branch_or_tag=""),
3535
])
3636

3737
# Act
3838
filtered = versions.filter(["3.13"])
3939

4040
# Assert
41-
assert filtered == [Version("3.13", status="security")]
41+
assert filtered == [Version(name="3.13", status="security-fixes", branch_or_tag="")]
4242

4343

4444
def test_filter_multiple() -> None:
4545
# Arrange
4646
versions = Versions([
47-
Version("3.14", status="feature"),
48-
Version("3.13", status="bugfix"),
49-
Version("3.12", status="bugfix"),
50-
Version("3.11", status="security"),
51-
Version("3.10", status="security"),
52-
Version("3.9", status="security"),
47+
Version(name="3.14", status="in development", branch_or_tag=""),
48+
Version(name="3.13", status="stable", branch_or_tag=""),
49+
Version(name="3.12", status="stable", branch_or_tag=""),
50+
Version(name="3.11", status="security-fixes", branch_or_tag=""),
51+
Version(name="3.10", status="security-fixes", branch_or_tag=""),
52+
Version(name="3.9", status="security-fixes", branch_or_tag=""),
5353
])
5454

5555
# Act
5656
filtered = versions.filter(["3.13", "3.14"])
5757

5858
# Assert
5959
assert filtered == [
60-
Version("3.14", status="feature"),
61-
Version("3.13", status="security"),
60+
Version(name="3.14", status="in development", branch_or_tag=""),
61+
Version(name="3.13", status="security-fixes", branch_or_tag=""),
6262
]

0 commit comments

Comments
 (0)