Skip to content

Commit ce29788

Browse files
committed
Create a dataclass for version info
Convert from fixed-shape dicts to a dataclass. Preserve almost all code semantics identically -- e.g., variable assignments in `readthedocs.api.v2.utils` may be micro-optimizations which could be important. The only intentional semantic change included here (other than the change in types) is in `readthedocs.projects.tasks.mixins`, where a list comprehension is replaced with a generator expression.
1 parent 632aec2 commit ce29788

File tree

5 files changed

+275
-430
lines changed

5 files changed

+275
-430
lines changed

readthedocs/api/v2/utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def sync_versions_to_db(project, versions, type):
4848
has_user_stable = False
4949
has_user_latest = False
5050
for version in versions:
51-
version_id = version["identifier"]
52-
version_name = version["verbose_name"]
51+
version_id = version.identifier
52+
version_name = version.verbose_name
5353
if version_name == STABLE_VERBOSE_NAME:
5454
has_user_stable = True
5555
created_version, created = _set_or_create_version(
@@ -170,8 +170,8 @@ def _set_or_create_version(project, slug, version_id, verbose_name, type_):
170170
def _get_deleted_versions_qs(project, tags_data, branches_data):
171171
# We use verbose_name for tags
172172
# because several tags can point to the same identifier.
173-
versions_tags = [version["verbose_name"] for version in tags_data]
174-
versions_branches = [version["identifier"] for version in branches_data]
173+
versions_tags = [version.verbose_name for version in tags_data]
174+
versions_branches = [version.identifier for version in branches_data]
175175

176176
to_delete_qs = (
177177
project.versions(manager=INTERNAL)

readthedocs/builds/tasks.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from readthedocs.integrations.models import HttpExchange
3434
from readthedocs.notifications.models import Notification
3535
from readthedocs.oauth.notifications import MESSAGE_OAUTH_BUILD_STATUS_FAILURE
36+
from readthedocs.projects.datatypes import ProjectVersionInfo
3637
from readthedocs.projects.models import Project
3738
from readthedocs.projects.models import WebHookEvent
3839
from readthedocs.storage import build_commands_storage
@@ -274,22 +275,33 @@ def delete_closed_external_versions(limit=200, days=30 * 3):
274275

275276

276277
@app.task(max_retries=1, default_retry_delay=60, queue="web")
277-
def sync_versions_task(project_pk, tags_data, branches_data, **kwargs):
278+
def sync_versions_task(
279+
project_pk: int,
280+
tags_data: list[ProjectVersionInfo],
281+
branches_data: list[ProjectVersionInfo],
282+
**kwargs: object,
283+
):
278284
"""
279285
Sync the version data in the repo (from build server) into our database.
280286
281-
Creates new Version objects for tags/branches that aren't tracked in the database,
282-
and deletes Version objects for tags/branches that don't exists in the repository.
287+
Creates new Version objects for tags/branches that aren't tracked in the
288+
database, and deletes Version objects for tags/branches that don't exists
289+
in the repository.
283290
284-
:param tags_data: List of dictionaries with ``verbose_name`` and ``identifier``
291+
:param tags_data: List of version descriptions
285292
Example: [
286-
{"verbose_name": "v1.0.0",
287-
"identifier": "67a9035990f44cb33091026d7453d51606350519"},
293+
ProjectVersionInfo(
294+
verbose_name="v1.0.0",
295+
identifier="67a9035990f44cb33091026d7453d51606350519",
296+
)
288297
].
289-
:param branches_data: Same as ``tags_data`` but for branches (branch name, branch identifier).
298+
:param branches_data: Same as ``tags_data`` but for branches (branch name,
299+
branch identifier).
290300
Example: [
291-
{"verbose_name": "latest",
292-
"identifier": "main"},
301+
ProjectVersionInfo(
302+
verbose_name="latest",
303+
identifier="main",
304+
)
293305
].
294306
:returns: `True` or `False` if the task succeeded.
295307
"""

readthedocs/projects/datatypes.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Simple datatypes defined using dataclasses, for describing project data.
2+
3+
Contrast this with the similar 'models' module, which defines Pydantic model
4+
classes.
5+
"""
6+
7+
import dataclasses
8+
9+
10+
@dataclasses.dataclass
11+
class ProjectVersionInfo:
12+
"""
13+
Version information for a project associated with a branch or tag.
14+
15+
The name fields is the end-user facing description, e.g., as seen in the
16+
version selector menu.
17+
18+
The identifier identifies the source for the build, e.g., a branch or
19+
commit hash.
20+
"""
21+
22+
verbose_name: str
23+
identifier: str

readthedocs/projects/tasks/mixins.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from readthedocs.builds.constants import STABLE_VERBOSE_NAME
88
from readthedocs.builds.models import APIVersion
99

10+
from ..datatypes import ProjectVersionInfo
1011
from ..exceptions import RepositoryError
1112
from ..models import Feature
1213

@@ -50,18 +51,18 @@ def sync_versions(self, vcs_repository):
5051
)
5152

5253
tags_data = [
53-
{
54-
"identifier": v.identifier,
55-
"verbose_name": v.verbose_name,
56-
}
54+
ProjectVersionInfo(
55+
verbose_name=v.verbose_name,
56+
identifier=v.identifier,
57+
)
5758
for v in tags
5859
]
5960

6061
branches_data = [
61-
{
62-
"identifier": v.identifier,
63-
"verbose_name": v.verbose_name,
64-
}
62+
ProjectVersionInfo(
63+
verbose_name=v.verbose_name,
64+
identifier=v.identifier,
65+
)
6566
for v in branches
6667
]
6768

@@ -78,7 +79,11 @@ def sync_versions(self, vcs_repository):
7879
branches_data=branches_data,
7980
)
8081

81-
def validate_duplicate_reserved_versions(self, tags_data, branches_data):
82+
def validate_duplicate_reserved_versions(
83+
self,
84+
tags_data: list[ProjectVersionInfo],
85+
branches_data: list[ProjectVersionInfo],
86+
) -> None:
8287
"""
8388
Check if there are duplicated names of reserved versions.
8489
@@ -88,8 +93,7 @@ def validate_duplicate_reserved_versions(self, tags_data, branches_data):
8893
8994
:param data: Dict containing the versions from tags and branches
9095
"""
91-
version_names = [version["verbose_name"] for version in tags_data + branches_data]
92-
counter = Counter(version_names)
96+
counter = Counter(v.verbose_name for v in tags_data + branches_data)
9397
for reserved_name in [STABLE_VERBOSE_NAME, LATEST_VERBOSE_NAME]:
9498
if counter[reserved_name] > 1:
9599
raise RepositoryError(

0 commit comments

Comments
 (0)