Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
17 changes: 14 additions & 3 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 -----------------------------------------------------
Expand All @@ -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 = {
Expand All @@ -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
Expand All @@ -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`
60 changes: 60 additions & 0 deletions pep-0012.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
===============

Expand Down
9 changes: 9 additions & 0 deletions pep_sphinx_extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_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
Expand Down Expand Up @@ -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
Expand Down
101 changes: 101 additions & 0 deletions pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py
Original file line number Diff line number Diff line change
@@ -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 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 "
"<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"]