From fdaa30b62d6be9db2a82727cdf2fc136d6317a67 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 29 Jan 2022 14:44:29 +0000 Subject: [PATCH 1/2] Add PEP titles and sections as reference title text --- pep_sphinx_extensions/__init__.py | 4 +++ .../pep_processor/parsing/pep_role.py | 28 +++++++++++---- .../transforms/pep_references.py | 36 +++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 pep_sphinx_extensions/pep_processor/transforms/pep_references.py diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index a281d6e6faf..55f5c93689b 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -13,6 +13,7 @@ from pep_sphinx_extensions.pep_processor.html import pep_html_translator from pep_sphinx_extensions.pep_processor.parsing import pep_parser from pep_sphinx_extensions.pep_processor.parsing import pep_role +from pep_sphinx_extensions.pep_processor.transforms import pep_references from pep_sphinx_extensions.pep_zero_generator.pep_index_generator import create_pep_zero if TYPE_CHECKING: @@ -28,6 +29,7 @@ def _depart_maths(): def _update_config_for_builder(app: Sphinx): + app.env.document_ids = {} # For PEPReferenceRoleTitleText if app.builder.name == "dirhtml": app.env.settings["pep_url"] = "../pep-{:0>4}" @@ -49,6 +51,8 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_role("pep", pep_role.PEPRole(), override=True) # Transform PEP references to links + app.add_post_transform(pep_references.PEPReferenceRoleTitleText) + # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py index e74ab0dd307..f7eccf41a78 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py @@ -1,14 +1,28 @@ +from docutils import nodes from sphinx import roles -class PEPRole(roles.PEP): +class PEPRole(roles.ReferenceRole): """Override the :pep: role""" - # TODO override the entire thing (internal should be True) - def build_uri(self) -> str: - """Get PEP URI from role text.""" + def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: + # Get PEP URI from role text. pep_str, _, fragment = self.target.partition("#") - pep_base = self.inliner.document.settings.pep_url.format(int(pep_str)) + pep_num = int(pep_str) + pep_base = self.inliner.document.settings.pep_url.format(pep_num) if fragment: - return f"{pep_base}#{fragment}" - return pep_base + ref_uri = f"{pep_base}#{fragment}" + else: + ref_uri = pep_base + if self.has_explicit_title: + title = self.title + else: + title = f"PEP {pep_num}" + try: + return [nodes.reference("", title, + internal=True, refuri=ref_uri, classes=["pep"], + _title_tuple=(pep_num, fragment))], [] + except ValueError: + msg = self.inliner.reporter.error(f'invalid PEP number {self.target}', line=self.lineno) + prb = self.inliner.problematic(self.rawtext, self.rawtext, msg) + return [prb], [msg] diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_references.py b/pep_sphinx_extensions/pep_processor/transforms/pep_references.py new file mode 100644 index 00000000000..079d13c8947 --- /dev/null +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_references.py @@ -0,0 +1,36 @@ +from pathlib import Path + +from docutils import nodes +from docutils import transforms + + +class PEPReferenceRoleTitleText(transforms.Transform): + """Add title text of document titles to reference role references.""" + + default_priority = 730 + + def apply(self) -> None: + if not Path(self.document["source"]).match("pep-*"): + return # not a PEP file, exit early + for node in self.document.findall(nodes.reference): + if "_title_tuple" not in node: + continue + + # get pep number and section target (fragment) + pep_num, fragment = node.attributes.pop("_title_tuple") + filename = f"pep-{pep_num:0>4}" + + # Cache target_ids + env = self.document.settings.env + try: + target_ids = env.document_ids[filename] + except KeyError: + env.document_ids[filename] = target_ids = env.get_doctree(filename).ids + + # Create title text string. We hijack the 'reftitle' attribute so + # that we don't have to change things in the HTML translator + node["reftitle"] = env.titles[filename].astext().replace(" – ", ", ") + try: + node["reftitle"] += f" § {target_ids[fragment][0].astext()}" + except KeyError: + pass From 226e8b3b06fd12c7d9047101ddd72d1583b45932 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sun, 27 Feb 2022 14:18:06 +0000 Subject: [PATCH 2/2] Address review --- .../pep_processor/parsing/pep_role.py | 25 ++++++++++++------- .../transforms/pep_references.py | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py index f7eccf41a78..18ecdc9829d 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py @@ -8,7 +8,12 @@ class PEPRole(roles.ReferenceRole): def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: # Get PEP URI from role text. pep_str, _, fragment = self.target.partition("#") - pep_num = int(pep_str) + try: + pep_num = int(pep_str) + except ValueError: + msg = self.inliner.reporter.error(f'invalid PEP number {self.target}', line=self.lineno) + prb = self.inliner.problematic(self.rawtext, self.rawtext, msg) + return [prb], [msg] pep_base = self.inliner.document.settings.pep_url.format(pep_num) if fragment: ref_uri = f"{pep_base}#{fragment}" @@ -18,11 +23,13 @@ def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: title = self.title else: title = f"PEP {pep_num}" - try: - return [nodes.reference("", title, - internal=True, refuri=ref_uri, classes=["pep"], - _title_tuple=(pep_num, fragment))], [] - except ValueError: - msg = self.inliner.reporter.error(f'invalid PEP number {self.target}', line=self.lineno) - prb = self.inliner.problematic(self.rawtext, self.rawtext, msg) - return [prb], [msg] + + return [ + nodes.reference( + "", title, + internal=True, + refuri=ref_uri, + classes=["pep"], + _title_tuple=(pep_num, fragment) + ) + ], [] diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_references.py b/pep_sphinx_extensions/pep_processor/transforms/pep_references.py index 079d13c8947..1e00e84cb20 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_references.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_references.py @@ -29,7 +29,7 @@ def apply(self) -> None: # Create title text string. We hijack the 'reftitle' attribute so # that we don't have to change things in the HTML translator - node["reftitle"] = env.titles[filename].astext().replace(" – ", ", ") + node["reftitle"] = env.titles[filename].astext() try: node["reftitle"] += f" § {target_ids[fragment][0].astext()}" except KeyError: