Skip to content

Commit c391284

Browse files
committed
Force re-read of newly added pages to build
This generates a couple of build warnings, because some references are missing on the first pass. This is expected, but we should either silence these errors or defer resolution of these references until later. This also doesn't include badges for similar reasons: because the references in the badges can't be resolved in the first pass, the badges are not linked to any pages.
1 parent dcebcd1 commit c391284

File tree

2 files changed

+91
-69
lines changed

2 files changed

+91
-69
lines changed

docs/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ Check out the `list of projects that use this extension <https://github.com/meli
2727
:titlesonly:
2828

2929
_tags/tagsindex.md
30+
31+
Indices and tables
32+
==================
33+
* :ref:`tags-index`

src/sphinx_tags/__init__.py

Lines changed: 87 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from sphinx.errors import ExtensionError
99
from sphinx.util.rst import textwidth
1010

11+
# from sphinx.addnodes import pending_xref
12+
from sphinx.builders import Builder
13+
1114
from pathlib import Path
1215
from docutils import nodes
1316
import os
@@ -71,22 +74,18 @@ def run(self):
7174
for tag in ",".join(self.content).split(",")
7275
]
7376
)
77+
7478
# Remove empty elements from page_tags
7579
# (can happen after _normalize_tag())
7680
page_tags = list(filter(None, page_tags))
77-
78-
# Leftover code from previous attempt:
79-
#settings = frontend.get_default_settings(Parser)
80-
#document = utils.new_document("tags", settings)
81-
#tags = Parser().parse(tagline, document)
82-
#print(f"Tags: {tags}")
83-
8481
global_tags = self.env.get_domain("tags")
8582
# Append this document to the list of documents containing this tag
8683
for tag in page_tags:
8784
global_tags.add_tag(tag, self.env.docname)
8885

8986
tag_dir = Path(self.env.app.srcdir) / self.env.app.config.tags_output_dir
87+
88+
# sphinx.addnodes.pending_xref(rawsource="", *children, **attributes)
9089
result = nodes.paragraph()
9190
result["classes"] = ["tags"]
9291
result += nodes.inline(text=f"{self.env.app.config.tags_intro_text} ")
@@ -126,6 +125,7 @@ def _get_plaintext_node(
126125
) -> List[nodes.Node]:
127126
"""Get a plaintext reference link for the given tag"""
128127
link = relative_tag_dir / f"{file_basename}.html"
128+
# return pending_xref(refuri=str(link), text=tag)
129129
return nodes.reference(refuri=str(link), text=tag)
130130

131131
def _get_badge_node(
@@ -139,7 +139,7 @@ def _get_badge_node(
139139
text_nodes, messages = self.state.inline_text("", self.lineno)
140140

141141
# Ref paths always use forward slashes, even on Windows
142-
tag_ref = f"{tag} <{relative_tag_dir.as_posix()}/{file_basename}>"
142+
tag_ref = f"{tag} <{relative_tag_dir.as_posix()}/{file_basename}.html>"
143143
tag_color = self._get_tag_color(tag)
144144
tag_badge = XRefBadgeRole(tag_color)
145145
return tag_badge(
@@ -161,12 +161,14 @@ def _get_tag_color(self, tag: str) -> str:
161161
return "primary"
162162

163163

164-
class PagesIndex(Index):
165-
"""A custom index that creates a pages matrix."""
164+
class TagsIndex(Index):
165+
"""A custom index that creates a page with all tags and corresponding pages
166+
where they are defined.
167+
"""
166168

167-
name = "tagpage"
168-
localname = "Page Index"
169-
shortname = "TagPage"
169+
name = "index"
170+
localname = "All tags"
171+
shortname = "TagsIndex"
170172

171173
def generate(
172174
self, docnames: Iterable[str] | None = None
@@ -177,14 +179,12 @@ def generate(
177179
pages = sorted(self.domain.get_objects(), key=lambda page: page[0])
178180

179181
# name, subtype, docname, anchor, extra, qualifier, description
180-
181182
for _name, dispname, typ, docname, anchor, _priority in pages:
182183
content[dispname[0].lower()].append(
183184
(dispname, 0, docname, anchor, docname, "", typ)
184185
)
185186

186187
# convert the dict to the sorted list of tuples expected
187-
188188
return sorted(content.items()), True
189189

190190

@@ -199,7 +199,7 @@ class TagsDomain(Domain):
199199
"tags": TagLinks,
200200
}
201201

202-
indices = [PagesIndex]
202+
indices = [TagsIndex]
203203

204204
# The values defined in initial_data will be copied to
205205
# env.domaindata[domain_name] as the initial data of the domain, and domain
@@ -211,7 +211,7 @@ class TagsDomain(Domain):
211211
}
212212

213213
def get_full_qualified_name(self, node):
214-
print(f"Node: {node}")
214+
# print(f"Node: {node}")
215215
return f"tags.{node.arguments[0]}"
216216

217217
def get_objects(self):
@@ -230,21 +230,35 @@ def add_tag(self, tagname, page):
230230
# Add this tag to the global list of tags
231231
# name, dispname, type, docname, anchor, priority
232232
self.data["tags"].append((tagname, tagname, "Tag", page, anchor, 0))
233+
# Create pages for each tag
234+
create_file(
235+
(tagname, self.data["entries"][tagname]),
236+
# self.app.config.tags_extension,
237+
".md",
238+
# Path(self.app.config.tags_output_dir),
239+
Path("_tags"),
240+
# self.app.srcdir,
241+
Path("docs"),
242+
# self.app.config.tags_page_title,
243+
"My tags",
244+
# self.app.config.tags_page_header,
245+
"With this tag",
246+
# self.app.config.tags_intro_text
247+
"Tags:",
248+
)
233249

234250
def add_tagpage(self, docname, tags):
235251
"""Add a new page of tags to domain"""
236-
name = f"tagpage.{docname}"
237-
anchor = f"tagpage-{docname}"
252+
name = f"index.{docname}"
253+
anchor = f"index-{docname}"
238254

239-
print(f"{self.data['tags']=}")
240255
# name, dispname, type, docname, anchor, priority
241256
self.data["pages"].append(
242-
(name, docname, "TagPage", self.env.docname, anchor, 0)
257+
(name, docname, "TagsIndex", self.env.docname, anchor, 0)
243258
)
244259

245260

246261
def create_file(
247-
app,
248262
tag: tuple,
249263
extension: List[str],
250264
tags_output_dir: Path,
@@ -278,17 +292,11 @@ def create_file(
278292
the words after which the pages with the tag are listed (e.g. "With this tag: Hello World")
279293
tag_intro_text: str
280294
the words after which the tags of a given page are listed (e.g. "Tags: programming, python")
281-
282-
self.tags = []
283-
if tagblock:
284-
self.tags = [_normalize_display_tag(tag) for tag in tagblock if tag]
285-
286295
"""
287296

288-
name = tag[0]
289-
file_basename = _normalize_tag(tag[0], dashes=True)
290-
291297
# Get sorted file paths for tag pages, relative to /docs/_tags
298+
file_basename = _normalize_tag(tag[0], dashes=True)
299+
name = _normalize_display_tag(tag[0])
292300
tag_page_paths = sorted([os.path.relpath(i, srcdir) for i in tag[1]])
293301
ref_label = f"sphx_tag_{file_basename}"
294302

@@ -355,10 +363,6 @@ def tagpage(tags, outdir, title, extension, tags_index_head):
355363
This page contains a list of all available tags.
356364
357365
"""
358-
359-
print(f"Tags: {tags=}")
360-
print(f"outdir: {outdir=}")
361-
362366
if "md" in extension:
363367
content = []
364368
content.append("(tagoverview)=")
@@ -371,9 +375,7 @@ def tagpage(tags, outdir, title, extension, tags_index_head):
371375
content.append(f"caption: {tags_index_head}")
372376
content.append("maxdepth: 1")
373377
content.append("---")
374-
for name, pages in tags.items():
375-
file_basename = _normalize_tag(name, dashes=True)
376-
content.append(f"{name} ({len(pages)}) <{file_basename}>")
378+
content.append("PLACEHOLDER")
377379
content.append("```")
378380
content.append("")
379381
filename = os.path.join(outdir, "tagsindex.md")
@@ -391,61 +393,77 @@ def tagpage(tags, outdir, title, extension, tags_index_head):
391393
content.append(f" :caption: {tags_index_head}")
392394
content.append(" :maxdepth: 1")
393395
content.append("")
394-
for name, pages in tags.items():
395-
file_basename = _normalize_tag(name, dashes=True)
396-
content.append(f" {name} ({len(pages)}) <{file_basename}.rst>")
396+
content.append("") # placeholder
397397
content.append("")
398398
filename = os.path.join(outdir, "tagsindex.rst")
399399

400400
with open(filename, "w", encoding="utf8") as f:
401401
f.write("\n".join(content))
402402

403403

404-
def update_tags(app):
404+
def update_tags(app, env):
405405
"""Update tags according to pages found"""
406406
if app.config.tags_create_tags:
407407
tags_output_dir = Path(app.config.tags_output_dir)
408-
409-
if not os.path.exists(os.path.join(app.srcdir, tags_output_dir)):
410-
os.makedirs(os.path.join(app.srcdir, tags_output_dir))
411-
412-
for file in os.listdir(os.path.join(app.srcdir, tags_output_dir)):
413-
if file.endswith("md") or file.endswith("rst"):
414-
os.remove(os.path.join(app.srcdir, tags_output_dir, file))
415-
416408
# Create pages for each tag
417409
global_tags = app.env.get_domain("tags").data["entries"]
418-
logger.info(f"Global tags: {global_tags=}", color="green")
419-
420-
for tag in global_tags.items():
421-
create_file(
422-
app,
423-
tag,
424-
app.config.tags_extension,
425-
tags_output_dir,
426-
app.srcdir,
427-
app.config.tags_page_title,
428-
app.config.tags_page_header,
429-
app.config.tags_intro_text,
410+
# Inject these into found_docs
411+
for docname, tags in global_tags.items():
412+
env.found_docs.add(docname)
413+
# Inject these into the tagsindex
414+
tags_output_dir = Path(app.config.tags_output_dir)
415+
outdir = os.path.join(app.srcdir, tags_output_dir)
416+
newcontent = []
417+
if "md" in app.config.tags_extension:
418+
filename = os.path.join(outdir, "tagsindex.md")
419+
taglist = "{name} ({len}) <{file_basename}>"
420+
else:
421+
filename = os.path.join(outdir, "tagsindex.rst")
422+
taglist = " {name} ({len}) <{file_basename}.rst>"
423+
424+
for name, pages in global_tags.items():
425+
file_basename = _normalize_tag(name, dashes=True)
426+
newcontent.append(
427+
taglist.format(name=name, len=len(pages), file_basename=file_basename)
430428
)
429+
with open(filename, "r", encoding="utf8") as f:
430+
content = f.read()
431+
with open(filename, "w", encoding="utf8") as f:
432+
f.write(content.replace("PLACEHOLDER", "\n".join(newcontent)))
433+
else:
434+
logger.info(
435+
"Tags were not created (tags_create_tags=False in conf.py)", color="white"
436+
)
437+
logger.info("Tags updated", color="white")
438+
439+
# Re-read files, create doctrees and add to env.found_docs and return
440+
# iterable of docnames to re-read for env-get-updated
441+
app.builder.read()
442+
return env.found_docs
443+
444+
445+
def prepare_tags(app):
446+
if app.config.tags_create_tags:
447+
tags_output_dir = Path(app.config.tags_output_dir)
448+
449+
if not os.path.exists(os.path.join(app.srcdir, tags_output_dir)):
450+
os.makedirs(os.path.join(app.srcdir, tags_output_dir))
431451

432452
# Create tags overview page
433453
tagpage(
434-
global_tags,
454+
[],
435455
os.path.join(app.srcdir, tags_output_dir),
436456
app.config.tags_overview_title,
437457
app.config.tags_extension,
438458
app.config.tags_index_head,
439459
)
440-
logger.info("Tags updated", color="white")
460+
461+
logger.info("Tags output dir created", color="green")
441462
else:
442463
logger.info(
443-
"Tags were not created (tags_create_tags=False in conf.py)", color="white"
464+
"Tags were not created (tags_create_tags=False in conf.py)", color="red"
444465
)
445466

446-
# Return iterable of docnames to re-read
447-
return os.listdir(os.path.join(app.srcdir, tags_output_dir))
448-
449467

450468
def setup(app):
451469
"""Setup for Sphinx."""
@@ -473,10 +491,10 @@ def setup(app):
473491
"html",
474492
)
475493

476-
# Update tags
477494
# Tags should be updated after sphinx-gallery is generated, on
478495
# builder-inited
479-
app.connect("builder-inited", update_tags)
496+
app.connect("builder-inited", prepare_tags)
497+
app.connect("env-get-updated", update_tags)
480498
app.add_directive("tags", TagLinks)
481499
app.add_domain(TagsDomain)
482500

0 commit comments

Comments
 (0)