Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,40 +54,46 @@ repos:
name: "Format with Black"
args:
- '--target-version=py311'
files: '^(peps/conf\.py|pep_sphinx_extensions/tests/.*)$'
exclude: '^peps/'

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.21
rev: v0.16.6
hooks:
- id: ruff-check
name: "Lint with Ruff"
args:
- '--exit-non-zero-on-fix'
files: '^pep_sphinx_extensions/tests/'
- id: ruff-format
name: "Format with Ruff"
args:
- '--check'
files: '^release_management/'
exclude: '^peps/'

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.37.4
rev: 0.38.0
hooks:
- id: check-dependabot
- id: check-github-workflows
- id: check-readthedocs

- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
hooks:
- id: actionlint

- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.26.1
rev: v1.30.1
hooks:
- id: zizmor

- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.29.4
hooks:
- id: pyproject-fmt

- repo: https://github.com/abravalheri/validate-pyproject
rev: '0.26'
hooks:
- id: validate-pyproject

- repo: https://github.com/tox-dev/tox-ini-fmt
rev: 1.7.2
rev: 1.9.0
hooks:
- id: tox-ini-fmt
name: "Format tox.ini"
Expand All @@ -114,7 +120,7 @@ repos:

# Manual codespell check
- repo: https://github.com/codespell-project/codespell
rev: v2.4.2
rev: v2.4.3
hooks:
- id: codespell
name: "Check for common misspellings in text files"
Expand Down
15 changes: 0 additions & 15 deletions .ruff.toml

This file was deleted.

42 changes: 29 additions & 13 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,33 @@ def create_parser():
parser = argparse.ArgumentParser(description="Build PEP documents")
# alternative builders:
builders = parser.add_mutually_exclusive_group()
builders.add_argument("-l", "--check-links", action="store_const",
dest="builder", const="linkcheck",
help='Check validity of links within PEP sources. '
'Cannot be used with "-f" or "-d".')
builders.add_argument("-f", "--build-files", action="store_const",
dest="builder", const="html",
help='Render PEPs to "pep-NNNN.html" files (default). '
'Cannot be used with "-d" or "-l".')
builders.add_argument("-d", "--build-dirs", action="store_const",
dest="builder", const="dirhtml",
help='Render PEPs to "index.html" files within "pep-NNNN" directories. '
'Cannot be used with "-f" or "-l".')
builders.add_argument(
"-l",
"--check-links",
action="store_const",
dest="builder",
const="linkcheck",
help="Check validity of links within PEP sources. "
'Cannot be used with "-f" or "-d".',
)
builders.add_argument(
"-f",
"--build-files",
action="store_const",
dest="builder",
const="html",
help='Render PEPs to "pep-NNNN.html" files (default). '
'Cannot be used with "-d" or "-l".',
)
builders.add_argument(
"-d",
"--build-dirs",
action="store_const",
dest="builder",
const="dirhtml",
help='Render PEPs to "index.html" files within "pep-NNNN" directories. '
'Cannot be used with "-f" or "-l".',
)

parser.add_argument(
"-o",
Expand All @@ -46,7 +61,8 @@ def create_index_file(html_root: Path, builder: str) -> None:
except FileNotFoundError:
return None
if builder == "dirhtml":
pep_zero_text = pep_zero_text.replace('="../', '="') # remove relative directory links
# remove relative directory links
pep_zero_text = pep_zero_text.replace('="../', '="')
html_root.joinpath("index.html").write_text(pep_zero_text, encoding="utf-8")


Expand Down
52 changes: 34 additions & 18 deletions check-peps.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"Title",
"Author",
"Sponsor",
"BDFL-Delegate", "PEP-Delegate",
"BDFL-Delegate",
"PEP-Delegate",
"Discussions-To",
"Status",
"Type",
Expand All @@ -57,18 +58,20 @@
REQUIRED_HEADERS = frozenset({"PEP", "Title", "Author", "Status", "Type", "Created"})

# See PEP 1 for the full list
ALL_STATUSES = frozenset({
"Accepted",
"Active",
"April Fool!",
"Deferred",
"Draft",
"Final",
"Provisional",
"Rejected",
"Superseded",
"Withdrawn",
})
ALL_STATUSES = frozenset(
{
"Accepted",
"Active",
"April Fool!",
"Deferred",
"Draft",
"Final",
"Provisional",
"Rejected",
"Superseded",
"Withdrawn",
}
)

# PEPs that are allowed to link directly to PEPs
SKIP_DIRECT_PEP_LINK_CHECK = frozenset({"0009", "0287", "0676", "0684", "8001"})
Expand All @@ -86,8 +89,12 @@
DISCOURSE_POST_PATTERN = re.compile(r"([\w\-]+/)?\d+(/\d+)?", DEFAULT_FLAGS)

MAILMAN_2_PATTERN = re.compile(r"[\w\-]+/\d{4}-[a-z]+/\d+\.html", DEFAULT_FLAGS)
MAILMAN_3_THREAD_PATTERN = re.compile(r"[\w\-]+@python\.org/thread/[a-z0-9]+/?", DEFAULT_FLAGS)
MAILMAN_3_MESSAGE_PATTERN = re.compile(r"[\w\-]+@python\.org/message/[a-z0-9]+/?(#[a-z0-9]+)?", DEFAULT_FLAGS)
MAILMAN_3_THREAD_PATTERN = re.compile(
r"[\w\-]+@python\.org/thread/[a-z0-9]+/?", DEFAULT_FLAGS
)
MAILMAN_3_MESSAGE_PATTERN = re.compile(
r"[\w\-]+@python\.org/message/[a-z0-9]+/?(#[a-z0-9]+)?", DEFAULT_FLAGS
)

# Controlled by the "--detailed" flag
DETAILED_ERRORS = False
Expand Down Expand Up @@ -203,7 +210,9 @@ def check_direct_links(line_num: int, line: str) -> MessageIterator:
yield line_num, "Use the :rfc:`NNN` role to refer to RFCs"


def _output_error(filename: Path, lines: Sequence[str], errors: Iterable[Message]) -> int:
def _output_error(
filename: Path, lines: Sequence[str], errors: Iterable[Message]
) -> int:
relative_filename = filename.relative_to(ROOT_DIR)
err_count = 0
for line_num, msg in errors:
Expand Down Expand Up @@ -501,7 +510,14 @@ def _invalid_domain(domain_part: str) -> bool:
return not root.isalnum() or not root.isascii()


def _thread(line_num: int, url: str, prefix: str, *, allow_message: bool = False, discussions_to: bool = False) -> MessageIterator:
def _thread(
line_num: int,
url: str,
prefix: str,
*,
allow_message: bool = False,
discussions_to: bool = False,
) -> MessageIterator:
if allow_message and discussions_to:
msg = "allow_message and discussions_to cannot both be True"
raise ValueError(msg)
Expand All @@ -523,7 +539,7 @@ def _thread(line_num: int, url: str, prefix: str, *, allow_message: bool = False

# We use ``str.rpartition`` as the topic name is optional
topic_name, _, topic_id = remainder.rpartition("/")
if topic_name == '' and _is_digits(topic_id):
if topic_name == "" and _is_digits(topic_id):
return
topic_name = topic_name.replace("-", "0").replace("_", "0")
# the topic name must not be entirely numeric
Expand Down
38 changes: 23 additions & 15 deletions pep_sphinx_extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from pep_sphinx_extensions.generate_rss import (
create_rss_feed,
get_from_doctree,
pep_abstract,
)
from pep_sphinx_extensions.pep_processor.html import (
pep_html_builder,
Expand All @@ -23,8 +22,7 @@
pep_parser,
pep_role,
)
from pep_sphinx_extensions.pep_processor.transforms import pep_footer
from pep_sphinx_extensions.pep_processor.transforms import pep_references
from pep_sphinx_extensions.pep_processor.transforms import pep_footer, pep_references
from pep_sphinx_extensions.pep_zero_generator.pep_index_generator import create_pep_zero

if TYPE_CHECKING:
Expand Down Expand Up @@ -85,35 +83,45 @@ def setup(app: Sphinx) -> dict[str, bool]:

app.add_source_parser(pep_parser.PEPParser) # Add PEP transforms

app.set_translator("html", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (html builder)
app.set_translator("dirhtml", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (dirhtml builder)
app.set_translator(
"html", pep_html_translator.PEPTranslator
) # Docutils Node Visitor overrides (html builder)
app.set_translator(
"dirhtml", pep_html_translator.PEPTranslator
) # Docutils Node Visitor overrides (dirhtml builder)

app.add_role("pep", pep_role.PEPRole(), override=True) # Transform PEP references to links
app.add_role(
"pep", pep_role.PEPRole(), override=True
) # Transform PEP references to links

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(
"pep-banner", pep_banner_directive.PEPBanner)
"canonical-pypa-spec", pep_banner_directive.CanonicalPyPASpecBanner
)
app.add_directive(
"canonical-doc", pep_banner_directive.CanonicalDocBanner)
app.add_directive(
"canonical-pypa-spec", pep_banner_directive.CanonicalPyPASpecBanner)
app.add_directive(
"canonical-typing-spec", pep_banner_directive.CanonicalTypingSpecBanner)
"canonical-typing-spec", pep_banner_directive.CanonicalTypingSpecBanner
)
app.add_directive("rejected", pep_banner_directive.RejectedBanner)
app.add_directive("superseded", pep_banner_directive.SupersededBanner)
app.add_directive("withdrawn", pep_banner_directive.WithdrawnBanner)

# Register event callbacks
app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used
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
app.connect('html-page-context', set_description)
app.connect("html-page-context", set_description)

# Mathematics rendering
inline_maths = HTMLTranslator.visit_math, None
block_maths = HTMLTranslator.visit_math_block, None
app.add_html_math_renderer("maths_to_html", inline_maths, block_maths) # Render maths to HTML
app.add_html_math_renderer(
"maths_to_html", inline_maths, block_maths
) # Render maths to HTML

# Parallel safety: https://www.sphinx-doc.org/en/master/extdev/index.html#extension-metadata
return {"parallel_read_safe": True, "parallel_write_safe": True}
16 changes: 11 additions & 5 deletions pep_sphinx_extensions/generate_rss.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


def _format_rfc_2822(datetime: dt.datetime) -> str:
datetime = datetime.replace(tzinfo=dt.timezone.utc)
datetime = datetime.replace(tzinfo=dt.UTC)
return format_datetime(datetime, usegmt=True)


Expand Down Expand Up @@ -64,14 +64,18 @@ def pep_abstract(document: nodes.document) -> str:
return para_node.astext().strip().replace("\n", " ")
return ""
if title_node.astext() == "Introduction":
introduction = node.next_node(nodes.paragraph).astext().strip().replace("\n", " ")
introduction = (
node.next_node(nodes.paragraph).astext().strip().replace("\n", " ")
)

return introduction


def _generate_items(doctree_dir: Path):
# get list of peps with creation time (from "Created:" string in pep source)
peps_with_dt = sorted((pep_creation(path), path) for path in doctree_dir.glob("pep-????.doctree"))
peps_with_dt = sorted(
(pep_creation(path), path) for path in doctree_dir.glob("pep-????.doctree")
)

# generate rss items for 10 most recent peps (in reverse order)
for datetime, full_path in reversed(peps_with_dt[-10:]):
Expand All @@ -86,7 +90,9 @@ def _generate_items(doctree_dir: Path):
author = get_from_doctree(full_path, "Author")
if "@" in author or " at " in author:
parsed_authors = getaddresses([author])
joined_authors = ", ".join(f"{name} ({email_address})" for name, email_address in parsed_authors)
joined_authors = ", ".join(
f"{name} ({email_address})" for name, email_address in parsed_authors
)
else:
joined_authors = author

Expand All @@ -104,7 +110,7 @@ def _generate_items(doctree_dir: Path):

def create_rss_feed(doctree_dir: Path, output_dir: Path):
# The rss envelope
last_build_date = _format_rfc_2822(dt.datetime.now(dt.timezone.utc))
last_build_date = _format_rfc_2822(dt.datetime.now(dt.UTC))
items = "\n".join(_generate_items(Path(doctree_dir)))
output = f"""\
<?xml version='1.0' encoding='UTF-8'?>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from docutils import nodes
from docutils.frontend import OptionParser
from sphinx.builders.dirhtml import DirectoryHTMLBuilder
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.writers.html import HTMLWriter

from sphinx.builders.dirhtml import DirectoryHTMLBuilder


class FileBuilder(StandaloneHTMLBuilder):
copysource = False # Prevent unneeded source copying - we link direct to GitHub
Expand All @@ -18,7 +17,9 @@ class FileBuilder(StandaloneHTMLBuilder):

def prepare_writing(self, _doc_names: set[str]) -> None:
self.docwriter = HTMLWriter(self)
_opt_parser = OptionParser([self.docwriter], defaults=self.env.settings, read_config_files=True)
_opt_parser = OptionParser(
[self.docwriter], defaults=self.env.settings, read_config_files=True
)
self.docsettings = _opt_parser.get_default_values()
self._orig_css_files = self._orig_js_files = []

Expand All @@ -36,7 +37,7 @@ def get_doc_context(self, docname: str, body: str, _metatags: str) -> dict:
if docname.startswith("pep-"):
del toc_tree[0] # remove contents node from PEPs
for node in toc_tree.findall(nodes.reference):
node["refuri"] = node["anchorname"] or '#' # fix targets
node["refuri"] = node["anchorname"] or "#" # fix targets
toc = self.render_partial(toc_tree)["fragment"]
else:
toc = "" # PEPs with no sections -- 9, 210
Expand Down
Loading
Loading