Skip to content

Commit 044aba7

Browse files
authored
Create a dataclass for Versions (#254)
1 parent e096701 commit 044aba7

File tree

1 file changed

+69
-54
lines changed

1 file changed

+69
-54
lines changed

build_docs.py

Lines changed: 69 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,9 @@
5050

5151
TYPE_CHECKING = False
5252
if TYPE_CHECKING:
53-
from collections.abc import Sequence
53+
from collections.abc import Iterator, Sequence
5454
from typing import Literal, TypeAlias
5555

56-
Versions: TypeAlias = Sequence["Version"]
5756
Languages: TypeAlias = Sequence["Language"]
5857

5958
try:
@@ -71,6 +70,57 @@
7170
HERE = Path(__file__).resolve().parent
7271

7372

73+
@dataclass(frozen=True, slots=True)
74+
class Versions:
75+
_seq: Sequence[Version]
76+
77+
def __iter__(self) -> Iterator[Version]:
78+
return iter(self._seq)
79+
80+
def __reversed__(self) -> Iterator[Version]:
81+
return reversed(self._seq)
82+
83+
@classmethod
84+
def from_json(cls, data) -> Versions:
85+
versions = sorted(
86+
[Version.from_json(name, release) for name, release in data.items()],
87+
key=Version.as_tuple,
88+
)
89+
return cls(versions)
90+
91+
def filter(self, branch: str = "") -> Sequence[Version]:
92+
"""Filter the given versions.
93+
94+
If *branch* is given, only *versions* matching *branch* are returned.
95+
96+
Else all live versions are returned (this means no EOL and no
97+
security-fixes branches).
98+
"""
99+
if branch:
100+
return [v for v in self if branch in (v.name, v.branch_or_tag)]
101+
return [v for v in self if v.status not in {"EOL", "security-fixes"}]
102+
103+
@property
104+
def current_stable(self) -> Version:
105+
"""Find the current stable CPython version."""
106+
return max((v for v in self if v.status == "stable"), key=Version.as_tuple)
107+
108+
@property
109+
def current_dev(self) -> Version:
110+
"""Find the current CPython version in development."""
111+
return max(self, key=Version.as_tuple)
112+
113+
def setup_indexsidebar(self, current: Version, dest_path: Path) -> None:
114+
"""Build indexsidebar.html for Sphinx."""
115+
template_path = HERE / "templates" / "indexsidebar.html"
116+
template = jinja2.Template(template_path.read_text(encoding="UTF-8"))
117+
rendered_template = template.render(
118+
current_version=current,
119+
versions=list(reversed(self)),
120+
)
121+
dest_path.write_text(rendered_template, encoding="UTF-8")
122+
123+
74124
@total_ordering
75125
class Version:
76126
"""Represents a CPython version and its documentation build dependencies."""
@@ -101,6 +151,17 @@ def __init__(self, name, *, status, branch_or_tag=None):
101151
def __repr__(self):
102152
return f"Version({self.name})"
103153

154+
def __eq__(self, other):
155+
return self.name == other.name
156+
157+
def __gt__(self, other):
158+
return self.as_tuple() > other.as_tuple()
159+
160+
@classmethod
161+
def from_json(cls, name, values):
162+
"""Loads a version from devguide's json representation."""
163+
return cls(name, status=values["status"], branch_or_tag=values["branch"])
164+
104165
@property
105166
def requirements(self):
106167
"""Generate the right requirements for this version.
@@ -144,29 +205,6 @@ def title(self):
144205
"""The title of this version's doc, for the sidebar."""
145206
return f"Python {self.name} ({self.status})"
146207

147-
@staticmethod
148-
def filter(versions, branch=None):
149-
"""Filter the given versions.
150-
151-
If *branch* is given, only *versions* matching *branch* are returned.
152-
153-
Else all live versions are returned (this means no EOL and no
154-
security-fixes branches).
155-
"""
156-
if branch:
157-
return [v for v in versions if branch in (v.name, v.branch_or_tag)]
158-
return [v for v in versions if v.status not in ("EOL", "security-fixes")]
159-
160-
@staticmethod
161-
def current_stable(versions):
162-
"""Find the current stable CPython version."""
163-
return max((v for v in versions if v.status == "stable"), key=Version.as_tuple)
164-
165-
@staticmethod
166-
def current_dev(versions):
167-
"""Find the current CPython version in development."""
168-
return max(versions, key=Version.as_tuple)
169-
170208
@property
171209
def picker_label(self):
172210
"""Forge the label of a version picker."""
@@ -176,27 +214,6 @@ def picker_label(self):
176214
return f"pre ({self.name})"
177215
return self.name
178216

179-
def setup_indexsidebar(self, versions: Versions, dest_path: Path):
180-
"""Build indexsidebar.html for Sphinx."""
181-
template_path = HERE / "templates" / "indexsidebar.html"
182-
template = jinja2.Template(template_path.read_text(encoding="UTF-8"))
183-
rendered_template = template.render(
184-
current_version=self,
185-
versions=versions[::-1],
186-
)
187-
dest_path.write_text(rendered_template, encoding="UTF-8")
188-
189-
@classmethod
190-
def from_json(cls, name, values):
191-
"""Loads a version from devguide's json representation."""
192-
return cls(name, status=values["status"], branch_or_tag=values["branch"])
193-
194-
def __eq__(self, other):
195-
return self.name == other.name
196-
197-
def __gt__(self, other):
198-
return self.as_tuple() > other.as_tuple()
199-
200217

201218
@dataclass(order=True, frozen=True, kw_only=True)
202219
class Language:
@@ -619,8 +636,8 @@ def build(self):
619636
+ ([""] if sys.platform == "darwin" else [])
620637
+ ["s/ *-A switchers=1//", self.checkout / "Doc" / "Makefile"]
621638
)
622-
self.version.setup_indexsidebar(
623-
self.versions,
639+
self.versions.setup_indexsidebar(
640+
self.version,
624641
self.checkout / "Doc" / "tools" / "templates" / "indexsidebar.html",
625642
)
626643
run_with_logging(
@@ -1013,7 +1030,7 @@ def build_docs(args) -> bool:
10131030
# This runs languages in config.toml order and versions newest first.
10141031
todo = [
10151032
(version, language)
1016-
for version in Version.filter(versions, args.branch)
1033+
for version in versions.filter(args.branch)
10171034
for language in reversed(Language.filter(languages, args.languages))
10181035
]
10191036
del args.branch
@@ -1081,9 +1098,7 @@ def parse_versions_from_devguide(http: urllib3.PoolManager) -> Versions:
10811098
"python/devguide/main/include/release-cycle.json",
10821099
timeout=30,
10831100
).json()
1084-
versions = [Version.from_json(name, release) for name, release in releases.items()]
1085-
versions.sort(key=Version.as_tuple)
1086-
return versions
1101+
return Versions.from_json(releases)
10871102

10881103

10891104
def parse_languages_from_config() -> Languages:
@@ -1170,7 +1185,7 @@ def major_symlinks(
11701185
- /es/3/ → /es/3.9/
11711186
"""
11721187
logging.info("Creating major version symlinks...")
1173-
current_stable = Version.current_stable(versions).name
1188+
current_stable = versions.current_stable.name
11741189
for language in languages:
11751190
symlink(
11761191
www_root,
@@ -1200,7 +1215,7 @@ def dev_symlink(
12001215
- /es/dev/ → /es/3.11/
12011216
"""
12021217
logging.info("Creating development version symlinks...")
1203-
current_dev = Version.current_dev(versions).name
1218+
current_dev = versions.current_dev.name
12041219
for language in languages:
12051220
symlink(
12061221
www_root,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy