Skip to content

Commit 64003b4

Browse files
committed
Added ability to build specific version with update_docs command
Also added an --interactive flag to prompt before building each version, making it possible to skip some.
1 parent 2b81dae commit 64003b4

File tree

3 files changed

+99
-32
lines changed

3 files changed

+99
-32
lines changed

docs/management/commands/update_docs.py

+52-22
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from django.conf import settings
1818
from django.core.management import BaseCommand, call_command
19+
from django.db.models import Q
1920
from django.utils.translation import to_locale
2021
from sphinx.application import Sphinx
2122
from sphinx.config import Config
@@ -39,6 +40,11 @@ def add_arguments(self, parser):
3940
"version is no longer supported."
4041
),
4142
)
43+
parser.add_argument(
44+
"--interactive",
45+
action="store_true",
46+
help="Ask before building each version",
47+
)
4248
parser.add_argument(
4349
"--update-index",
4450
action="store_true",
@@ -53,40 +59,58 @@ def add_arguments(self, parser):
5359
default=False,
5460
help="Also invalidate downstream caches for any changed doc versions.",
5561
)
62+
parser.add_argument(
63+
"args",
64+
metavar="versions",
65+
nargs="*",
66+
help="Which version to rebuild (all by default)",
67+
)
68+
69+
def _get_doc_releases(self, versions, options):
70+
"""
71+
Return a DocumentRelease queryset of all the versions that should be
72+
built, based on the arguments received on the command line.
73+
"""
74+
default_docs_version = DocumentRelease.objects.get(
75+
is_default=True
76+
).release.version
77+
78+
# Somehow, bizarely, there's a bug in Sphinx such that if I try to
79+
# build 1.0 before other versions, things fail in weird ways. However,
80+
# building newer versions first works. I suspect Sphinx is hanging onto
81+
# some global state. Anyway, we can work around it by making sure that
82+
# "dev" builds before "1.0". This is ugly, but oh well.
83+
queryset = DocumentRelease.objects.order_by("-release")
5684

57-
def handle(self, **kwargs):
85+
# Skip translated non-stable versions to avoid a crash:
86+
# https://github.com/django/djangoproject.com/issues/627
87+
queryset = queryset.filter(Q(lang="en") | Q(release=default_docs_version))
88+
89+
if options["language"]:
90+
queryset = queryset.filter(lang=options["language"])
91+
92+
if versions:
93+
queryset = queryset.by_versions(*versions)
94+
95+
return queryset
96+
97+
def handle(self, *versions, **kwargs):
5898
self.verbosity = kwargs["verbosity"]
5999
self.update_index = kwargs["update_index"]
60100
self.purge_cache = kwargs["purge_cache"]
61101

62102
self.default_builders = ["json", "djangohtml"]
63-
default_docs_version = DocumentRelease.objects.get(
64-
is_default=True
65-
).release.version
66103

67104
# Keep track of which Git sources have been updated, e.g.,
68105
# {'1.8': True} if the 1.8 docs updated.
69106
self.release_docs_changed = {}
70107
# Only update the index if some docs rebuild.
71108
self.update_index_required = False
72109

73-
# Somehow, bizarely, there's a bug in Sphinx such that if I try to
74-
# build 1.0 before other versions, things fail in weird ways. However,
75-
# building newer versions first works. I suspect Sphinx is hanging onto
76-
# some global state. Anyway, we can work around it by making sure that
77-
# "dev" builds before "1.0". This is ugly, but oh well.
78-
doc_releases = DocumentRelease.objects.order_by("-release")
79-
if kwargs["language"]:
80-
doc_releases = doc_releases.filter(lang=kwargs["language"])
81-
for release in doc_releases:
82-
# Skip translated non-stable versions to avoid a crash:
83-
# https://github.com/django/djangoproject.com/issues/627
84-
if (
85-
release.lang != "en"
86-
and not release.release.version == default_docs_version
87-
):
88-
continue
89-
self.build_doc_release(release, force=kwargs["force"])
110+
for release in self._get_doc_releases(versions, kwargs):
111+
self.build_doc_release(
112+
release, force=kwargs["force"], interactive=kwargs["interactive"]
113+
)
90114

91115
if self.update_index_required:
92116
call_command("update_index", **{"verbosity": self.verbosity})
@@ -106,10 +130,16 @@ def handle(self, **kwargs):
106130
if self.verbosity >= 1:
107131
self.stdout.write("No docs changes; skipping cache purge.")
108132

109-
def build_doc_release(self, release, force=False):
133+
def build_doc_release(self, release, force=False, interactive=False):
110134
# Skip not supported releases.
111135
if not release.is_supported and not force:
112136
return
137+
if interactive:
138+
prompt = (
139+
f"About to start building docs for release {release}. Continue? Y/n "
140+
)
141+
if input(prompt).upper() not in {"", "Y", "YES", "OUI"}:
142+
return
113143
if self.verbosity >= 1:
114144
self.stdout.write(f"Starting update for {release} at {datetime.now()}...")
115145

docs/models.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
)
3535

3636

37-
class DocumentReleaseManager(models.Manager):
37+
class DocumentReleaseQuerySet(models.QuerySet):
3838
def current(self, lang="en"):
3939
current = self.get(is_default=True)
4040
if lang != "en":
@@ -58,11 +58,21 @@ def current_version(self):
5858
)
5959
return current_version
6060

61-
def by_version(self, version):
62-
return self.filter(
63-
**{"release__isnull": True} if version == "dev" else {"release": version}
61+
def _by_version_Q(self, version):
62+
return (
63+
models.Q(release__isnull=True)
64+
if version == "dev"
65+
else models.Q(release=version)
6466
)
6567

68+
def by_version(self, version):
69+
return self.filter(self._by_version_Q(version))
70+
71+
def by_versions(self, *versions):
72+
if not versions:
73+
raise ValueError("by_versions() takes at least one argument")
74+
return self.filter(reduce(operator.or_, map(self._by_version_Q, versions)))
75+
6676
def get_by_version_and_lang(self, version, lang):
6777
return self.by_version(version).get(lang=lang)
6878

@@ -86,7 +96,7 @@ class DocumentRelease(models.Model):
8696
)
8797
is_default = models.BooleanField(default=False)
8898

89-
objects = DocumentReleaseManager()
99+
objects = DocumentReleaseQuerySet.as_manager()
90100

91101
class Meta:
92102
unique_together = ("lang", "release")

docs/tests/test_models.py

+32-5
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,43 @@ def setUpTestData(cls):
111111
r2 = Release.objects.create(version="2.0")
112112
DocumentRelease.objects.bulk_create(
113113
DocumentRelease(lang=lang, release=release)
114-
for lang, release in [("en", r1), ("en", r2), ("sv", r1), ("ar", r1)]
114+
for lang, release in [
115+
("en", None),
116+
("en", r1),
117+
("en", r2),
118+
("sv", r1),
119+
("ar", r1),
120+
]
115121
)
116122

117123
def test_by_version(self):
118-
doc_releases = DocumentRelease.objects.by_version("1.0")
119-
self.assertEqual(
120-
{(r.lang, r.release.version) for r in doc_releases},
121-
{("en", "1.0"), ("sv", "1.0"), ("ar", "1.0")},
124+
self.assertQuerySetEqual(
125+
DocumentRelease.objects.by_version("1.0"),
126+
[("en", "1.0"), ("sv", "1.0"), ("ar", "1.0")],
127+
transform=attrgetter("lang", "version"),
128+
ordered=False,
129+
)
130+
131+
def test_by_version_dev(self):
132+
self.assertQuerySetEqual(
133+
DocumentRelease.objects.by_version("dev"),
134+
[("en", "dev")],
135+
transform=attrgetter("lang", "version"),
136+
ordered=False,
122137
)
123138

139+
def test_by_versions(self):
140+
self.assertQuerySetEqual(
141+
DocumentRelease.objects.by_versions("1.0", "dev"),
142+
[("en", "dev"), ("en", "1.0"), ("sv", "1.0"), ("ar", "1.0")],
143+
transform=attrgetter("lang", "version"),
144+
ordered=False,
145+
)
146+
147+
def test_by_versions_empty(self):
148+
with self.assertRaises(ValueError):
149+
DocumentRelease.objects.by_versions()
150+
124151
def test_get_by_version_and_lang_exists(self):
125152
doc = DocumentRelease.objects.get_by_version_and_lang("1.0", "en")
126153
self.assertEqual(doc.release.version, "1.0")

0 commit comments

Comments
 (0)