Skip to content

Commit fc13b1a

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 a9aa9cb commit fc13b1a

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
@@ -34,6 +34,7 @@
3434
from readthedocs.integrations.models import HttpExchange
3535
from readthedocs.notifications.models import Notification
3636
from readthedocs.oauth.notifications import MESSAGE_OAUTH_BUILD_STATUS_FAILURE
37+
from readthedocs.projects.datatypes import ProjectVersionInfo
3738
from readthedocs.projects.models import Project
3839
from readthedocs.projects.models import WebHookEvent
3940
from readthedocs.storage import build_commands_storage
@@ -275,22 +276,33 @@ def delete_closed_external_versions(limit=200, days=30 * 3):
275276

276277

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

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

@@ -57,18 +58,18 @@ def sync_versions(self, vcs_repository):
5758
return
5859

5960
tags_data = [
60-
{
61-
"identifier": v.identifier,
62-
"verbose_name": v.verbose_name,
63-
}
61+
ProjectVersionInfo(
62+
verbose_name=v.verbose_name,
63+
identifier=v.identifier,
64+
)
6465
for v in tags
6566
]
6667

6768
branches_data = [
68-
{
69-
"identifier": v.identifier,
70-
"verbose_name": v.verbose_name,
71-
}
69+
ProjectVersionInfo(
70+
verbose_name=v.verbose_name,
71+
identifier=v.identifier,
72+
)
7273
for v in branches
7374
]
7475

@@ -85,7 +86,11 @@ def sync_versions(self, vcs_repository):
8586
branches_data=branches_data,
8687
)
8788

88-
def validate_duplicate_reserved_versions(self, tags_data, branches_data):
89+
def validate_duplicate_reserved_versions(
90+
self,
91+
tags_data: list[ProjectVersionInfo],
92+
branches_data: list[ProjectVersionInfo],
93+
) -> None:
8994
"""
9095
Check if there are duplicated names of reserved versions.
9196
@@ -95,8 +100,7 @@ def validate_duplicate_reserved_versions(self, tags_data, branches_data):
95100
96101
:param data: Dict containing the versions from tags and branches
97102
"""
98-
version_names = [version["verbose_name"] for version in tags_data + branches_data]
99-
counter = Counter(version_names)
103+
counter = Counter(v.verbose_name for v in tags_data + branches_data)
100104
for reserved_name in [STABLE_VERBOSE_NAME, LATEST_VERBOSE_NAME]:
101105
if counter[reserved_name] > 1:
102106
raise RepositoryError(

0 commit comments

Comments
 (0)