Infra: Use Docutils' `list-table` syntax for PEP 0 (#2616)
This commit is contained in:
parent
ab4b55cebb
commit
f7c9e62c9f
|
@ -7,83 +7,17 @@ from docutils import transforms
|
|||
class PEPZero(transforms.Transform):
|
||||
"""Schedule PEP 0 processing."""
|
||||
|
||||
# Run during sphinx post processing
|
||||
# Run during sphinx post-processing
|
||||
default_priority = 760
|
||||
|
||||
def apply(self) -> None:
|
||||
# Walk document and then remove this node
|
||||
visitor = PEPZeroSpecial(self.document)
|
||||
self.document.walk(visitor)
|
||||
# Walk document and mask email addresses if present.
|
||||
for reference_node in self.document.findall(nodes.reference):
|
||||
reference_node.replace_self(_mask_email(reference_node))
|
||||
# Remove this node
|
||||
self.startnode.parent.remove(self.startnode)
|
||||
|
||||
|
||||
class PEPZeroSpecial(nodes.SparseNodeVisitor):
|
||||
"""Perform the special processing needed by PEP 0.
|
||||
|
||||
- Mask email addresses.
|
||||
- Link PEP numbers and PEP titles in the table to the PEPs themselves.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, document: nodes.document):
|
||||
super().__init__(document)
|
||||
self.pep_table: int = 0
|
||||
self.entry: int = 0
|
||||
self.ref: str | None = None
|
||||
|
||||
def unknown_visit(self, node: nodes.Node) -> None:
|
||||
"""No processing for undefined node types."""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def visit_reference(node: nodes.reference) -> None:
|
||||
"""Mask email addresses if present."""
|
||||
node.replace_self(_mask_email(node))
|
||||
|
||||
@staticmethod
|
||||
def visit_field_list(node: nodes.field_list) -> None:
|
||||
"""Skip PEP headers."""
|
||||
if "rfc2822" in node["classes"]:
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_tgroup(self, node: nodes.tgroup) -> None:
|
||||
"""Set column counter and PEP table marker."""
|
||||
self.pep_table = node["cols"] == 4
|
||||
self.entry = 0 # reset column number
|
||||
|
||||
def visit_colspec(self, node: nodes.colspec) -> None:
|
||||
self.entry += 1
|
||||
if self.pep_table and self.entry == 2:
|
||||
node["classes"].append("num")
|
||||
|
||||
def visit_row(self, _node: nodes.row) -> None:
|
||||
self.entry = 0 # reset column number
|
||||
self.ref = None # Reset PEP URL
|
||||
|
||||
def visit_entry(self, node: nodes.entry) -> None:
|
||||
self.entry += 1
|
||||
if not self.pep_table:
|
||||
return
|
||||
if self.entry == 2 and len(node) == 1:
|
||||
node["classes"].append("num")
|
||||
# if this is the PEP number column, replace the number with a link to the PEP
|
||||
para = node[0]
|
||||
if isinstance(para, nodes.paragraph) and len(para) == 1:
|
||||
pep_str = para.astext()
|
||||
try:
|
||||
pep_num = int(pep_str)
|
||||
except ValueError:
|
||||
return
|
||||
self.ref = self.document.settings.pep_url.format(pep_num)
|
||||
para[0] = nodes.reference("", pep_str, refuri=self.ref)
|
||||
elif self.entry == 3 and len(node) == 1 and self.ref:
|
||||
# If this is the PEP title column, add a link to the PEP
|
||||
para = node[0]
|
||||
if isinstance(para, nodes.paragraph) and len(para) == 1:
|
||||
pep_title = para.astext()
|
||||
para[0] = nodes.reference("", pep_title, refuri=self.ref)
|
||||
|
||||
|
||||
def _mask_email(ref: nodes.reference) -> nodes.reference:
|
||||
"""Mask the email address in `ref` and return a replacement node.
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ table td {
|
|||
text-align: left;
|
||||
padding: 0.25rem 0.5rem 0.2rem;
|
||||
}
|
||||
table tr td.num {
|
||||
table.pep-zero-table tr td:nth-child(2) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table td + td {
|
||||
|
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
from email.parser import HeaderParser
|
||||
from pathlib import Path
|
||||
import re
|
||||
import textwrap
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from pep_sphinx_extensions.pep_zero_generator.author import parse_author_email
|
||||
|
@ -114,13 +113,14 @@ class PEP:
|
|||
def __eq__(self, other):
|
||||
return self.number == other.number
|
||||
|
||||
def details(self, *, title_length) -> dict[str, str | int]:
|
||||
@property
|
||||
def details(self) -> dict[str, str | int]:
|
||||
"""Return the line entry for the PEP."""
|
||||
return {
|
||||
# how the type is to be represented in the index
|
||||
"type": self.pep_type[0].upper(),
|
||||
"number": self.number,
|
||||
"title": _title_abbr(self.title, title_length),
|
||||
"title": self.title,
|
||||
# how the status should be represented in the index
|
||||
"status": " " if self.status in HIDE_STATUS else self.status[0].upper(),
|
||||
# the author list as a comma-separated with only last names
|
||||
|
@ -172,11 +172,3 @@ def _parse_author(data: str) -> list[tuple[str, str]]:
|
|||
if author_list:
|
||||
break
|
||||
return author_list
|
||||
|
||||
|
||||
def _title_abbr(title, title_length) -> str:
|
||||
"""Shorten the title to be no longer than the max title length."""
|
||||
if len(title) <= title_length:
|
||||
return title
|
||||
wrapped_title, *_excess = textwrap.wrap(title, title_length - 4)
|
||||
return f"{wrapped_title} ..."
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import functools
|
||||
from typing import TYPE_CHECKING
|
||||
import unicodedata
|
||||
|
||||
|
@ -26,16 +25,6 @@ from pep_sphinx_extensions.pep_zero_generator.errors import PEPError
|
|||
if TYPE_CHECKING:
|
||||
from pep_sphinx_extensions.pep_zero_generator.parser import PEP
|
||||
|
||||
title_length = 55
|
||||
author_length = 40
|
||||
table_separator = "== ==== " + "="*title_length + " " + "="*author_length
|
||||
|
||||
# column format is called as a function with a mapping containing field values
|
||||
column_format = functools.partial(
|
||||
"{type}{status}{number: >5} {title: <{title_length}} {authors}".format,
|
||||
title_length=title_length
|
||||
)
|
||||
|
||||
header = f"""\
|
||||
PEP: 0
|
||||
Title: Index of Python Enhancement Proposals (PEPs)
|
||||
|
@ -80,21 +69,27 @@ class PEPZeroWriter:
|
|||
def emit_newline(self) -> None:
|
||||
self.output.append("")
|
||||
|
||||
def emit_table_separator(self) -> None:
|
||||
self.output.append(table_separator)
|
||||
|
||||
def emit_author_table_separator(self, max_name_len: int) -> None:
|
||||
author_table_separator = "=" * max_name_len + " " + "=" * len("email address")
|
||||
self.output.append(author_table_separator)
|
||||
|
||||
def emit_pep_row(self, pep_details: dict[str, int | str]) -> None:
|
||||
self.emit_text(column_format(**pep_details))
|
||||
def emit_pep_row(self, *, type: str, status: str, number: int, title: str, authors: str) -> None:
|
||||
self.emit_text(f" * - {type}{status}")
|
||||
self.emit_text(f" - :pep:`{number} <{number}>`")
|
||||
self.emit_text(f" - :pep:`{title.replace('`', '')} <{number}>`")
|
||||
self.emit_text(f" - {authors}")
|
||||
|
||||
def emit_column_headers(self) -> None:
|
||||
"""Output the column headers for the PEP indices."""
|
||||
self.emit_table_separator()
|
||||
self.emit_pep_row({"status": ".", "type": ".", "number": "PEP", "title": "PEP Title", "authors": "PEP Author(s)"})
|
||||
self.emit_table_separator()
|
||||
self.emit_text(".. list-table::")
|
||||
self.emit_text(" :header-rows: 1")
|
||||
self.emit_text(" :widths: auto")
|
||||
self.emit_text(" :class: pep-zero-table")
|
||||
self.emit_newline()
|
||||
self.emit_text(" * - ")
|
||||
self.emit_text(" - PEP")
|
||||
self.emit_text(" - PEP Title")
|
||||
self.emit_text(" - PEP Author(s)")
|
||||
|
||||
def emit_title(self, text: str, *, symbol: str = "=") -> None:
|
||||
self.output.append(text)
|
||||
|
@ -108,8 +103,13 @@ class PEPZeroWriter:
|
|||
self.emit_subtitle(category)
|
||||
self.emit_column_headers()
|
||||
for pep in peps:
|
||||
self.output.append(column_format(**pep.details(title_length=title_length)))
|
||||
self.emit_table_separator()
|
||||
self.emit_pep_row(**pep.details)
|
||||
# list-table must have at least one body row
|
||||
if len(peps) == 0:
|
||||
self.emit_text(" * -")
|
||||
self.emit_text(" -")
|
||||
self.emit_text(" -")
|
||||
self.emit_text(" -")
|
||||
self.emit_newline()
|
||||
|
||||
def write_pep0(self, peps: list[PEP]):
|
||||
|
@ -146,18 +146,16 @@ class PEPZeroWriter:
|
|||
self.emit_title("Numerical Index")
|
||||
self.emit_column_headers()
|
||||
for pep in peps:
|
||||
self.emit_pep_row(pep.details(title_length=title_length))
|
||||
self.emit_pep_row(**pep.details)
|
||||
|
||||
self.emit_table_separator()
|
||||
self.emit_newline()
|
||||
|
||||
# Reserved PEP numbers
|
||||
self.emit_title("Reserved PEP Numbers")
|
||||
self.emit_column_headers()
|
||||
for number, claimants in sorted(self.RESERVED.items()):
|
||||
self.emit_pep_row({"type": ".", "status": ".", "number": number, "title": "RESERVED", "authors": claimants})
|
||||
self.emit_pep_row(type="", status="", number=number, title="RESERVED", authors=claimants)
|
||||
|
||||
self.emit_table_separator()
|
||||
self.emit_newline()
|
||||
|
||||
# PEP types key
|
||||
|
|
|
@ -28,21 +28,14 @@ def test_pep_equal():
|
|||
assert pep_a == pep_b
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_input, expected",
|
||||
[
|
||||
(80, "Style Guide for Python Code"),
|
||||
(10, "Style ..."),
|
||||
],
|
||||
)
|
||||
def test_pep_details(test_input, expected):
|
||||
def test_pep_details():
|
||||
pep8 = parser.PEP(Path("pep-0008.txt"), AUTHORS_OVERRIDES)
|
||||
|
||||
assert pep8.details(title_length=test_input) == {
|
||||
assert pep8.details == {
|
||||
"authors": "GvR, Warsaw, Coghlan",
|
||||
"number": 8,
|
||||
"status": " ",
|
||||
"title": expected,
|
||||
"title": "Style Guide for Python Code",
|
||||
"type": "P",
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue