From 452eff8840d323b7e54c7f18d7f9a91da4bd9ab9 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 7 Jul 2022 22:55:49 -0500 Subject: [PATCH 1/6] Infra: Add new roles to point readers to a PEP's canonical content --- pep_sphinx_extensions/__init__.py | 7 ++ .../pep_canonical_content_directive.py | 100 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 17ed80b2cb1..2a72069495e 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -10,6 +10,7 @@ from pep_sphinx_extensions.pep_processor.html import pep_html_builder from pep_sphinx_extensions.pep_processor.html import pep_html_translator +from pep_sphinx_extensions.pep_processor.parsing import pep_canonical_content_directive 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 @@ -93,6 +94,12 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_post_transform(pep_references.PEPReferenceRoleTitleText) + # Register custom directives + app.add_directive( + 'canonical-content', pep_canonical_content_directive.CanonicalContent) + app.add_directive( + 'canonical-content-pypa', pep_canonical_content_directive.CanonicalContentPyPA) + # 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_canonical_content_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py new file mode 100644 index 00000000000..ecdb1e220cc --- /dev/null +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py @@ -0,0 +1,100 @@ +"""Roles to insert custom admonitions pointing readers to canonical content.""" + +from __future__ import annotations + +from docutils import nodes +from docutils.parsers import rst + + +class CanonicalContent(rst.Directive): + """Insert an admonition pointing readers to a PEP's canonical content.""" + + has_content = True + required_arguments = 0 + optional_arguments = 2 + final_argument_whitespace = True + option_spec = {} + + default_canonical_link_title = "the Python Docs" + default_canonical_link_uri = "https://docs.python.org/" + generic_canonical_link_title = "this link" + use_default_link = True + + admonition_pre_template = ( + "This PEP is a historical document; the up-to-date, canonical " + "documentation can now be found at `{link_title} <{link_uri}>`__." + ) + admonition_pre_text = ( + "This PEP is a historical document; the up-to-date, canonical " + "documentation can now be found elsewhere." + ) + admonition_post_text = ( + "See :pep:`1` for how to propose changes." + ) + + + def run(self) -> list[nodes.admonition]: + + if self.arguments or (not self.content and self.use_default_link): + if self.arguments: + link_title = self.generic_canonical_link_title + link_uri = self.arguments[0] + if len(self.arguments) >= 2: + link_title = self.arguments[1] + else: + link_title = self.default_canonical_link_title + link_uri = self.default_canonical_link_uri + + pre_text = self.admonition_pre_template.format( + link_uri=link_uri, link_title=link_title) + + else: + pre_text = self.admonition_pre_text + + pre_text_node = nodes.paragraph(pre_text) + pre_text_node.line = self.lineno + pre_node, pre_msg = self.state.inline_text(pre_text, self.lineno) + pre_text_node.extend(pre_node + pre_msg) + + post_text = self.admonition_post_text + post_text_node = nodes.paragraph(post_text) + post_text_node.line = self.lineno + post_node, post_msg = self.state.inline_text(post_text, self.lineno) + post_text_node.extend(post_node + post_msg) + + source_lines = [pre_text] + list(self.content or []) + [post_text] + admonition_node = nodes.important( + "\n".join(source_lines), classes=["canonical-content"]) + + admonition_node.append(pre_text_node) + if self.content: + self.state.nested_parse( + self.content, self.content_offset, admonition_node) + admonition_node.append(post_text_node) + + return [admonition_node] + + +class CanonicalContentPyPA(CanonicalContent): + """Insert a specialized admonition for PyPA packaging specifications.""" + + generic_canonical_link_title = "packaging" + use_default_link = False + + admonition_pre_template = ( + "This PEP is a historical document; the up-to-date, canonical " + "`{link_title} specification " + "`__ " + "is maintained on the `PyPA specs page " + "`__." + ) + admonition_pre_text = ( + "This PEP is a historical document; the up-to-date, canonical " + "specification is maintained on the `PyPA specs page " + "`__." + ) + admonition_post_text = ( + "See the `PyPA specification update process " + "`__ " + "for how to propose changes." + ) From e2cda206f9823fd02233412816e12ab60a457b41 Mon Sep 17 00:00:00 2001 From: CAM Gerlach Date: Fri, 8 Jul 2022 17:02:05 -0500 Subject: [PATCH 2/6] Infra: Clean up default link title & condition check in canonical directive Co-authored-by: Hugo van Kemenade --- .../pep_processor/parsing/pep_canonical_content_directive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py index ecdb1e220cc..dad7cc7896b 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py @@ -15,7 +15,7 @@ class CanonicalContent(rst.Directive): final_argument_whitespace = True option_spec = {} - default_canonical_link_title = "the Python Docs" + default_canonical_link_title = "the Python docs" default_canonical_link_uri = "https://docs.python.org/" generic_canonical_link_title = "this link" use_default_link = True @@ -35,7 +35,7 @@ class CanonicalContent(rst.Directive): def run(self) -> list[nodes.admonition]: - if self.arguments or (not self.content and self.use_default_link): + if self.arguments or (self.use_default_link and not self.content): if self.arguments: link_title = self.generic_canonical_link_title link_uri = self.arguments[0] From 3f50ab6068c1f1e3cc1a911b98f18823642f5ac6 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 22 Jul 2022 18:17:22 -0500 Subject: [PATCH 3/6] Infra: Rename and refactor canonical ref directive to inject reST links --- pep_sphinx_extensions/__init__.py | 8 +- ...t_directive.py => pep_banner_directive.py} | 83 ++++++++++--------- 2 files changed, 47 insertions(+), 44 deletions(-) rename pep_sphinx_extensions/pep_processor/parsing/{pep_canonical_content_directive.py => pep_banner_directive.py} (62%) diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 2a72069495e..554e7b0460e 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -10,7 +10,7 @@ from pep_sphinx_extensions.pep_processor.html import pep_html_builder from pep_sphinx_extensions.pep_processor.html import pep_html_translator -from pep_sphinx_extensions.pep_processor.parsing import pep_canonical_content_directive +from pep_sphinx_extensions.pep_processor.parsing import pep_banner_directive 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 @@ -96,9 +96,11 @@ def setup(app: Sphinx) -> dict[str, bool]: # Register custom directives app.add_directive( - 'canonical-content', pep_canonical_content_directive.CanonicalContent) + "pep-banner", pep_banner_directive.PEPBanner) app.add_directive( - 'canonical-content-pypa', pep_canonical_content_directive.CanonicalContentPyPA) + "canonical-doc", pep_banner_directive.CanonicalDocBanner) + app.add_directive( + "canonical-pypa-spec", pep_banner_directive.CanonicalPyPASpecBanner) # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py similarity index 62% rename from pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py rename to pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py index dad7cc7896b..1d338d34c87 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_canonical_content_directive.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py @@ -6,48 +6,32 @@ from docutils.parsers import rst -class CanonicalContent(rst.Directive): - """Insert an admonition pointing readers to a PEP's canonical content.""" +PYPA_SPEC_BASE_URL = "https://packaging.python.org/en/latest/specifications/" + + +class PEPBanner(rst.Directive): + """Insert a special banner admonition in a PEP document.""" has_content = True required_arguments = 0 - optional_arguments = 2 + optional_arguments = 1 final_argument_whitespace = True option_spec = {} - default_canonical_link_title = "the Python docs" - default_canonical_link_uri = "https://docs.python.org/" - generic_canonical_link_title = "this link" - use_default_link = True + admonition_pre_template = "" + admonition_pre_text = "" + admonition_post_text = "" - admonition_pre_template = ( - "This PEP is a historical document; the up-to-date, canonical " - "documentation can now be found at `{link_title} <{link_uri}>`__." - ) - admonition_pre_text = ( - "This PEP is a historical document; the up-to-date, canonical " - "documentation can now be found elsewhere." - ) - admonition_post_text = ( - "See :pep:`1` for how to propose changes." - ) + admonition_class = nodes.important + css_classes = ["pep-banner"] def run(self) -> list[nodes.admonition]: - if self.arguments or (self.use_default_link and not self.content): - if self.arguments: - link_title = self.generic_canonical_link_title - link_uri = self.arguments[0] - if len(self.arguments) >= 2: - link_title = self.arguments[1] - else: - link_title = self.default_canonical_link_title - link_uri = self.default_canonical_link_uri - + if self.arguments: + link_content = self.arguments[0] pre_text = self.admonition_pre_template.format( - link_uri=link_uri, link_title=link_title) - + link_content=link_content) else: pre_text = self.admonition_pre_text @@ -63,8 +47,8 @@ def run(self) -> list[nodes.admonition]: post_text_node.extend(post_node + post_msg) source_lines = [pre_text] + list(self.content or []) + [post_text] - admonition_node = nodes.important( - "\n".join(source_lines), classes=["canonical-content"]) + admonition_node = self.admonition_class( + "\n".join(source_lines), classes=self.css_classes) admonition_node.append(pre_text_node) if self.content: @@ -75,26 +59,43 @@ def run(self) -> list[nodes.admonition]: return [admonition_node] -class CanonicalContentPyPA(CanonicalContent): - """Insert a specialized admonition for PyPA packaging specifications.""" - - generic_canonical_link_title = "packaging" - use_default_link = False +class CanonicalDocBanner(PEPBanner): + """Insert an admonition pointing readers to a PEP's canonical docs.""" admonition_pre_template = ( "This PEP is a historical document; the up-to-date, canonical " - "`{link_title} specification " - "`__ " + "documentation can now be found at {link_content}." + ) + admonition_pre_text = ( + "This PEP is a historical document; the up-to-date, canonical " + "documentation can now be found elsewhere." + ) + admonition_post_text = ( + "See :pep:`1` for how to propose changes." + ) + + css_classes = [*PEPBanner.css_classes, "canonical-doc"] + + + +class CanonicalPyPASpecBanner(PEPBanner): + """Insert a specialized admonition for PyPA packaging specifications.""" + + admonition_pre_template = ( + "This PEP is a historical document; the up-to-date, canonical spec, " + "{link_content}, " "is maintained on the `PyPA specs page " - "`__." + f"<{PYPA_SPEC_BASE_URL}>`__." ) admonition_pre_text = ( "This PEP is a historical document; the up-to-date, canonical " "specification is maintained on the `PyPA specs page " - "`__." + f"<{PYPA_SPEC_BASE_URL}>`__." ) admonition_post_text = ( "See the `PyPA specification update process " "`__ " "for how to propose changes." ) + + css_classes = [*PEPBanner.css_classes, "canonical-pypa-spec"] From 1a9162eb4db4f9d885902f01a0a272afe7b2caa6 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 22 Jul 2022 19:04:50 -0500 Subject: [PATCH 4/6] Infra: Add intersphinx support for Python docs and PyPUG --- conf.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/conf.py b/conf.py index 3d39d1182be..44ff3e3eb99 100644 --- a/conf.py +++ b/conf.py @@ -10,7 +10,7 @@ # Add 'include_patterns' as a config variable from sphinx.config import Config -Config.config_values['include_patterns'] = [], 'env', [] +Config.config_values["include_patterns"] = [], "env", [] del Config # -- Project information ----------------------------------------------------- @@ -21,7 +21,11 @@ # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. -extensions = ["pep_sphinx_extensions", "sphinx.ext.githubpages"] +extensions = [ +"pep_sphinx_extensions", +"sphinx.ext.intersphinx", +"sphinx.ext.githubpages", +] # The file extensions of source files. Sphinx uses these suffixes as sources. source_suffix = { @@ -46,6 +50,13 @@ "pep-0012/pep-NNNN.rst", ] +# Intersphinx configuration +intersphinx_mapping = { + 'python': ('https://docs.python.org/3/', None), + 'packaging': ('https://packaging.python.org/en/latest/', None), +} +intersphinx_disabled_reftypes = [] + # -- Options for HTML output ------------------------------------------------- # HTML output settings @@ -60,4 +71,4 @@ html_baseurl = "https://peps.python.org" # to create the CNAME file gettext_auto_build = False # speed-ups -templates_path = ['pep_sphinx_extensions/pep_theme/templates'] # Theme template relative paths from `confdir` +templates_path = ["pep_sphinx_extensions/pep_theme/templates"] # Theme template relative paths from `confdir` From 786cf337ab648c7628385531c6defbf316746bba Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 25 Jul 2022 10:02:02 -0500 Subject: [PATCH 5/6] PEP 12: Add new section documenting canonical directives & intersphinx --- pep-0012.rst | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/pep-0012.rst b/pep-0012.rst index 11b7e339939..3c488e10c8e 100644 --- a/pep-0012.rst +++ b/pep-0012.rst @@ -649,6 +649,66 @@ If you find that you need to use a backslash in your text, consider using inline literals or a literal block instead. +Canonical Documentation and Intersphinx +--------------------------------------- + +As :pep:`PEP 1 describes <1#pep-maintenance>`, +PEPs are considered historical documents once marked Final, +and their canonical documentation/specification should be moved elsewhere. +To indicate this, use the ``canonical-docs`` directive +or an appropriate subclass +(currently ``canonical-pypa-spec`` for packaging standards). + +Furthermore, you can use +`Intersphinx references +`_ +to other Sphinx sites, +currently the `Python documentation `_ +and `packaging.python.org `_, +to easily cross-reference pages, sections and Python/C objects. +This works with both the "canonical" directives and anywhere in your PEP. + +Add the directive between the headers and the first section of the PEP +(typically the Abstract) +and pass as an argument an Intersphinx reference of the canonical doc/spec +(or if the target is not on a Sphinx site, a `reST hyperlink `__). + +For example, +to create a banner pointing to the :mod:`python:sqlite3` docs, +you would write the following:: + + .. canonical-doc:: :mod:`python:sqlite3` + +which would generate the banner: + + .. canonical-doc:: :mod:`python:sqlite3` + +Or for a PyPA spec, +such as the :ref:`packaging:core-metadata`, +you would use:: + + .. canonical-pypa-spec:: :ref:`packaging:core-metadata` + +which renders as: + + .. canonical-pypa-spec:: :ref:`packaging:core-metadata` + +The argument accepts arbitrary reST, +so you can include multiple linked docs/specs and name them whatever you like, +and you can also include directive content that will be inserted into the text. +The following advanced example:: + + .. canonical-doc:: the :ref:`python:sqlite3-connection-objects` and :exc:`python:~sqlite3.DataError` docs + + Also, see the :ref:`Data Persistence docs ` for other examples. + +would render as: + + .. canonical-doc:: the :ref:`python:sqlite3-connection-objects` and :exc:`python:sqlite3.DataError` docs + + Also, see the :ref:`Data Persistence docs ` for other examples. + + Habits to Avoid =============== From debdfd928aa9a5c85f14b0cc5fb911215ecb927d Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sat, 30 Jul 2022 10:43:12 -0500 Subject: [PATCH 6/6] Infra: Split banner preface into two sentances & use logical line breaks --- .../parsing/pep_banner_directive.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py index 1d338d34c87..4a42186be02 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py @@ -63,12 +63,13 @@ class CanonicalDocBanner(PEPBanner): """Insert an admonition pointing readers to a PEP's canonical docs.""" admonition_pre_template = ( - "This PEP is a historical document; the up-to-date, canonical " - "documentation can now be found at {link_content}." + "This PEP is a historical document. " + "The up-to-date, canonical documentation can now be found " + "at {link_content}." ) admonition_pre_text = ( - "This PEP is a historical document; the up-to-date, canonical " - "documentation can now be found elsewhere." + "This PEP is a historical document. " + "The up-to-date, canonical documentation can now be found elsewhere." ) admonition_post_text = ( "See :pep:`1` for how to propose changes." @@ -82,15 +83,14 @@ class CanonicalPyPASpecBanner(PEPBanner): """Insert a specialized admonition for PyPA packaging specifications.""" admonition_pre_template = ( - "This PEP is a historical document; the up-to-date, canonical spec, " - "{link_content}, " - "is maintained on the `PyPA specs page " - f"<{PYPA_SPEC_BASE_URL}>`__." + "This PEP is a historical document. " + "the up-to-date, canonical spec, {link_content}, is maintained on " + f"the `PyPA specs page <{PYPA_SPEC_BASE_URL}>`__." ) admonition_pre_text = ( - "This PEP is a historical document; the up-to-date, canonical " - "specification is maintained on the `PyPA specs page " - f"<{PYPA_SPEC_BASE_URL}>`__." + "This PEP is a historical document. " + "The up-to-date, canonical specification is maintained on " + f"the `PyPA specs page <{PYPA_SPEC_BASE_URL}>`__." ) admonition_post_text = ( "See the `PyPA specification update process "