58
58
import sys
59
59
from bisect import bisect_left as bisect
60
60
from contextlib import contextmanager , suppress
61
- from functools import total_ordering
62
61
from pathlib import Path
63
62
from string import Template
64
63
from time import perf_counter , sleep
@@ -103,11 +102,23 @@ def __reversed__(self) -> Iterator[Version]:
103
102
104
103
@classmethod
105
104
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 ))
111
122
112
123
def filter (self , branches : Sequence [str ] = ()) -> Sequence [Version ]:
113
124
"""Filter the given versions.
@@ -143,10 +154,14 @@ def setup_indexsidebar(self, current: Version, dest_path: Path) -> None:
143
154
dest_path .write_text (rendered_template , encoding = "UTF-8" )
144
155
145
156
146
- @total_ordering
157
+ @dataclasses . dataclass ( frozen = True , kw_only = True , slots = True )
147
158
class Version :
148
159
"""Represents a CPython version and its documentation build dependencies."""
149
160
161
+ name : str
162
+ branch_or_tag : str | None
163
+ status : str
164
+
150
165
STATUSES = {"EOL" , "security-fixes" , "stable" , "pre-release" , "in development" }
151
166
152
167
# Those synonyms map branch status vocabulary found in the devguide
@@ -159,19 +174,6 @@ class Version:
159
174
"prerelease" : "pre-release" ,
160
175
}
161
176
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
177
def __repr__ (self ) -> str :
176
178
return f"Version({ self .name } )"
177
179
@@ -181,11 +183,6 @@ def __eq__(self, other: Version) -> bool:
181
183
def __gt__ (self , other : Version ) -> bool :
182
184
return self .as_tuple () > other .as_tuple ()
183
185
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
-
189
186
@property
190
187
def requirements (self ) -> list [str ]:
191
188
"""Generate the right requirements for this version.
0 commit comments