5858import sys
5959from bisect import bisect_left as bisect
6060from contextlib import contextmanager , suppress
61- from functools import total_ordering
6261from pathlib import Path
6362from string import Template
6463from 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 , branch_or_tag = branch , status = status ))
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 )
147158class Version :
148159 """Represents a CPython version and its documentation build dependencies."""
149160
161+ name : str
162+ branch_or_tag : str | None
163+ status : 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,19 +174,6 @@ 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-
175177 def __repr__ (self ) -> str :
176178 return f"Version({ self .name } )"
177179
@@ -181,11 +183,6 @@ def __eq__(self, other: Version) -> bool:
181183 def __gt__ (self , other : Version ) -> bool :
182184 return self .as_tuple () > other .as_tuple ()
183185
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-
189186 @property
190187 def requirements (self ) -> list [str ]:
191188 """Generate the right requirements for this version.
0 commit comments