Skip to content

Commit f7c9e62

Browse files
authored
Infra: Use Docutils' list-table syntax for PEP 0 (#2616)
1 parent ab4b55c commit f7c9e62

File tree

5 files changed

+35
-118
lines changed

5 files changed

+35
-118
lines changed

pep_sphinx_extensions/pep_processor/transforms/pep_zero.py

Lines changed: 5 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,83 +7,17 @@
77
class PEPZero(transforms.Transform):
88
"""Schedule PEP 0 processing."""
99

10-
# Run during sphinx post processing
10+
# Run during sphinx post-processing
1111
default_priority = 760
1212

1313
def apply(self) -> None:
14-
# Walk document and then remove this node
15-
visitor = PEPZeroSpecial(self.document)
16-
self.document.walk(visitor)
14+
# Walk document and mask email addresses if present.
15+
for reference_node in self.document.findall(nodes.reference):
16+
reference_node.replace_self(_mask_email(reference_node))
17+
# Remove this node
1718
self.startnode.parent.remove(self.startnode)
1819

1920

20-
class PEPZeroSpecial(nodes.SparseNodeVisitor):
21-
"""Perform the special processing needed by PEP 0.
22-
23-
- Mask email addresses.
24-
- Link PEP numbers and PEP titles in the table to the PEPs themselves.
25-
26-
"""
27-
28-
def __init__(self, document: nodes.document):
29-
super().__init__(document)
30-
self.pep_table: int = 0
31-
self.entry: int = 0
32-
self.ref: str | None = None
33-
34-
def unknown_visit(self, node: nodes.Node) -> None:
35-
"""No processing for undefined node types."""
36-
pass
37-
38-
@staticmethod
39-
def visit_reference(node: nodes.reference) -> None:
40-
"""Mask email addresses if present."""
41-
node.replace_self(_mask_email(node))
42-
43-
@staticmethod
44-
def visit_field_list(node: nodes.field_list) -> None:
45-
"""Skip PEP headers."""
46-
if "rfc2822" in node["classes"]:
47-
raise nodes.SkipNode
48-
49-
def visit_tgroup(self, node: nodes.tgroup) -> None:
50-
"""Set column counter and PEP table marker."""
51-
self.pep_table = node["cols"] == 4
52-
self.entry = 0 # reset column number
53-
54-
def visit_colspec(self, node: nodes.colspec) -> None:
55-
self.entry += 1
56-
if self.pep_table and self.entry == 2:
57-
node["classes"].append("num")
58-
59-
def visit_row(self, _node: nodes.row) -> None:
60-
self.entry = 0 # reset column number
61-
self.ref = None # Reset PEP URL
62-
63-
def visit_entry(self, node: nodes.entry) -> None:
64-
self.entry += 1
65-
if not self.pep_table:
66-
return
67-
if self.entry == 2 and len(node) == 1:
68-
node["classes"].append("num")
69-
# if this is the PEP number column, replace the number with a link to the PEP
70-
para = node[0]
71-
if isinstance(para, nodes.paragraph) and len(para) == 1:
72-
pep_str = para.astext()
73-
try:
74-
pep_num = int(pep_str)
75-
except ValueError:
76-
return
77-
self.ref = self.document.settings.pep_url.format(pep_num)
78-
para[0] = nodes.reference("", pep_str, refuri=self.ref)
79-
elif self.entry == 3 and len(node) == 1 and self.ref:
80-
# If this is the PEP title column, add a link to the PEP
81-
para = node[0]
82-
if isinstance(para, nodes.paragraph) and len(para) == 1:
83-
pep_title = para.astext()
84-
para[0] = nodes.reference("", pep_title, refuri=self.ref)
85-
86-
8721
def _mask_email(ref: nodes.reference) -> nodes.reference:
8822
"""Mask the email address in `ref` and return a replacement node.
8923

pep_sphinx_extensions/pep_theme/static/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ table td {
233233
text-align: left;
234234
padding: 0.25rem 0.5rem 0.2rem;
235235
}
236-
table tr td.num {
236+
table.pep-zero-table tr td:nth-child(2) {
237237
white-space: nowrap;
238238
}
239239
table td + td {

pep_sphinx_extensions/pep_zero_generator/parser.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from email.parser import HeaderParser
66
from pathlib import Path
77
import re
8-
import textwrap
98
from typing import TYPE_CHECKING
109

1110
from pep_sphinx_extensions.pep_zero_generator.author import parse_author_email
@@ -114,13 +113,14 @@ def __lt__(self, other: PEP) -> bool:
114113
def __eq__(self, other):
115114
return self.number == other.number
116115

117-
def details(self, *, title_length) -> dict[str, str | int]:
116+
@property
117+
def details(self) -> dict[str, str | int]:
118118
"""Return the line entry for the PEP."""
119119
return {
120120
# how the type is to be represented in the index
121121
"type": self.pep_type[0].upper(),
122122
"number": self.number,
123-
"title": _title_abbr(self.title, title_length),
123+
"title": self.title,
124124
# how the status should be represented in the index
125125
"status": " " if self.status in HIDE_STATUS else self.status[0].upper(),
126126
# the author list as a comma-separated with only last names
@@ -172,11 +172,3 @@ def _parse_author(data: str) -> list[tuple[str, str]]:
172172
if author_list:
173173
break
174174
return author_list
175-
176-
177-
def _title_abbr(title, title_length) -> str:
178-
"""Shorten the title to be no longer than the max title length."""
179-
if len(title) <= title_length:
180-
return title
181-
wrapped_title, *_excess = textwrap.wrap(title, title_length - 4)
182-
return f"{wrapped_title} ..."

pep_sphinx_extensions/pep_zero_generator/writer.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44

55
import datetime
6-
import functools
76
from typing import TYPE_CHECKING
87
import unicodedata
98

@@ -26,16 +25,6 @@
2625
if TYPE_CHECKING:
2726
from pep_sphinx_extensions.pep_zero_generator.parser import PEP
2827

29-
title_length = 55
30-
author_length = 40
31-
table_separator = "== ==== " + "="*title_length + " " + "="*author_length
32-
33-
# column format is called as a function with a mapping containing field values
34-
column_format = functools.partial(
35-
"{type}{status}{number: >5} {title: <{title_length}} {authors}".format,
36-
title_length=title_length
37-
)
38-
3928
header = f"""\
4029
PEP: 0
4130
Title: Index of Python Enhancement Proposals (PEPs)
@@ -80,21 +69,27 @@ def emit_text(self, content: str) -> None:
8069
def emit_newline(self) -> None:
8170
self.output.append("")
8271

83-
def emit_table_separator(self) -> None:
84-
self.output.append(table_separator)
85-
8672
def emit_author_table_separator(self, max_name_len: int) -> None:
8773
author_table_separator = "=" * max_name_len + " " + "=" * len("email address")
8874
self.output.append(author_table_separator)
8975

90-
def emit_pep_row(self, pep_details: dict[str, int | str]) -> None:
91-
self.emit_text(column_format(**pep_details))
76+
def emit_pep_row(self, *, type: str, status: str, number: int, title: str, authors: str) -> None:
77+
self.emit_text(f" * - {type}{status}")
78+
self.emit_text(f" - :pep:`{number} <{number}>`")
79+
self.emit_text(f" - :pep:`{title.replace('`', '')} <{number}>`")
80+
self.emit_text(f" - {authors}")
9281

9382
def emit_column_headers(self) -> None:
9483
"""Output the column headers for the PEP indices."""
95-
self.emit_table_separator()
96-
self.emit_pep_row({"status": ".", "type": ".", "number": "PEP", "title": "PEP Title", "authors": "PEP Author(s)"})
97-
self.emit_table_separator()
84+
self.emit_text(".. list-table::")
85+
self.emit_text(" :header-rows: 1")
86+
self.emit_text(" :widths: auto")
87+
self.emit_text(" :class: pep-zero-table")
88+
self.emit_newline()
89+
self.emit_text(" * - ")
90+
self.emit_text(" - PEP")
91+
self.emit_text(" - PEP Title")
92+
self.emit_text(" - PEP Author(s)")
9893

9994
def emit_title(self, text: str, *, symbol: str = "=") -> None:
10095
self.output.append(text)
@@ -108,8 +103,13 @@ def emit_pep_category(self, category: str, peps: list[PEP]) -> None:
108103
self.emit_subtitle(category)
109104
self.emit_column_headers()
110105
for pep in peps:
111-
self.output.append(column_format(**pep.details(title_length=title_length)))
112-
self.emit_table_separator()
106+
self.emit_pep_row(**pep.details)
107+
# list-table must have at least one body row
108+
if len(peps) == 0:
109+
self.emit_text(" * -")
110+
self.emit_text(" -")
111+
self.emit_text(" -")
112+
self.emit_text(" -")
113113
self.emit_newline()
114114

115115
def write_pep0(self, peps: list[PEP]):
@@ -146,18 +146,16 @@ def write_pep0(self, peps: list[PEP]):
146146
self.emit_title("Numerical Index")
147147
self.emit_column_headers()
148148
for pep in peps:
149-
self.emit_pep_row(pep.details(title_length=title_length))
149+
self.emit_pep_row(**pep.details)
150150

151-
self.emit_table_separator()
152151
self.emit_newline()
153152

154153
# Reserved PEP numbers
155154
self.emit_title("Reserved PEP Numbers")
156155
self.emit_column_headers()
157156
for number, claimants in sorted(self.RESERVED.items()):
158-
self.emit_pep_row({"type": ".", "status": ".", "number": number, "title": "RESERVED", "authors": claimants})
157+
self.emit_pep_row(type="", status="", number=number, title="RESERVED", authors=claimants)
159158

160-
self.emit_table_separator()
161159
self.emit_newline()
162160

163161
# PEP types key

pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,14 @@ def test_pep_equal():
2828
assert pep_a == pep_b
2929

3030

31-
@pytest.mark.parametrize(
32-
"test_input, expected",
33-
[
34-
(80, "Style Guide for Python Code"),
35-
(10, "Style ..."),
36-
],
37-
)
38-
def test_pep_details(test_input, expected):
31+
def test_pep_details():
3932
pep8 = parser.PEP(Path("pep-0008.txt"), AUTHORS_OVERRIDES)
4033

41-
assert pep8.details(title_length=test_input) == {
34+
assert pep8.details == {
4235
"authors": "GvR, Warsaw, Coghlan",
4336
"number": 8,
4437
"status": " ",
45-
"title": expected,
38+
"title": "Style Guide for Python Code",
4639
"type": "P",
4740
}
4841

0 commit comments

Comments
 (0)