Infra: Add new directives to point to PEP's canonical content (#2702)

As originally discussed in #2692 , this adds a three new custom directives intended to be used at the top of PEPs:

* `pep-banner`, a generic admonition banner that can be subclassed with an arbitrary message,
* `canonical-docs`, which renders a banner linking to a `Final` PEP's canonical documentation/specification.
* `canonical-pypa-spec`, a banner for packaging interoperability PEPs linking to the canonical PyPA specs.

Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
This commit is contained in:
CAM Gerlach 2022-08-02 22:48:56 -05:00 committed by GitHub
parent d31806fe1e
commit f87de8284c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 184 additions and 3 deletions

17
conf.py
View File

@ -10,7 +10,7 @@ sys.path.append(str(Path("pep_sphinx_extensions").absolute()))
# 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 @@ master_doc = "contents"
# -- 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 @@ exclude_patterns = [
"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_permalinks = False # handled in the PEPContents transform
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`

View File

@ -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
<https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html>`_
to other Sphinx sites,
currently the `Python documentation <https://docs.python.org/>`_
and `packaging.python.org <https://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 <Hyperlinks_>`__).
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 <persistence>` 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 <persistence>` for other examples.
Habits to Avoid
===============

View File

@ -10,6 +10,7 @@ from sphinx import project
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_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
@ -93,6 +94,14 @@ def setup(app: Sphinx) -> dict[str, bool]:
app.add_post_transform(pep_references.PEPReferenceRoleTitleText)
# Register custom directives
app.add_directive(
"pep-banner", pep_banner_directive.PEPBanner)
app.add_directive(
"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
app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook

View File

@ -0,0 +1,101 @@
"""Roles to insert custom admonitions pointing readers to canonical content."""
from __future__ import annotations
from docutils import nodes
from docutils.parsers import rst
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 = 1
final_argument_whitespace = True
option_spec = {}
admonition_pre_template = ""
admonition_pre_text = ""
admonition_post_text = ""
admonition_class = nodes.important
css_classes = ["pep-banner"]
def run(self) -> list[nodes.admonition]:
if self.arguments:
link_content = self.arguments[0]
pre_text = self.admonition_pre_template.format(
link_content=link_content)
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 = self.admonition_class(
"\n".join(source_lines), classes=self.css_classes)
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 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}."
)
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 "
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 "
f"the `PyPA specs page <{PYPA_SPEC_BASE_URL}>`__."
)
admonition_post_text = (
"See the `PyPA specification update process "
"<https://www.pypa.io/en/latest/specifications/#handling-fixes-and-other-minor-updates>`__ "
"for how to propose changes."
)
css_classes = [*PEPBanner.css_classes, "canonical-pypa-spec"]