Skip to content

Commit ff4dd8d

Browse files
committed
Support subindicies
1 parent 5232fda commit ff4dd8d

File tree

4 files changed

+116
-61
lines changed

4 files changed

+116
-61
lines changed

pep_sphinx_extensions/pep_zero_generator/parser.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import csv
56
from email.parser import HeaderParser
67
from pathlib import Path
78
import re
@@ -23,6 +24,14 @@
2324
from pep_sphinx_extensions.pep_zero_generator.author import Author
2425

2526

27+
# AUTHOR_OVERRIDES.csv is an exception file for PEP0 name parsing
28+
AUTHOR_OVERRIDES: dict[str, dict[str, str]] = {}
29+
with open("AUTHOR_OVERRIDES.csv", "r", encoding="utf-8") as f:
30+
for line in csv.DictReader(f):
31+
full_name = line.pop("Overridden Name")
32+
AUTHOR_OVERRIDES[full_name] = line
33+
34+
2635
class PEP:
2736
"""Representation of PEPs.
2837
@@ -38,7 +47,7 @@ class PEP:
3847
# The required RFC 822 headers for all PEPs.
3948
required_headers = {"PEP", "Title", "Author", "Status", "Type", "Created"}
4049

41-
def __init__(self, filename: Path, authors_overrides: dict):
50+
def __init__(self, filename: Path):
4251
"""Init object from an open PEP file object.
4352
4453
pep_file is full text of the PEP file, filename is path of the PEP file, author_lookup is author exceptions file
@@ -89,7 +98,11 @@ def __init__(self, filename: Path, authors_overrides: dict):
8998
self.status: str = status
9099

91100
# Parse PEP authors
92-
self.authors: list[Author] = _parse_authors(self, metadata["Author"], authors_overrides)
101+
self.authors: list[Author] = _parse_authors(self, metadata["Author"], AUTHOR_OVERRIDES)
102+
103+
# Topic (for sub-indicies)
104+
_topic = metadata.get("Topic", "").lower().split(",")
105+
self.topics: set[str] = {topic for topic_raw in _topic if (topic := topic_raw.strip())}
93106

94107
# Other headers
95108
self.created = metadata["Created"]
@@ -127,6 +140,23 @@ def details(self, *, title_length) -> dict[str, str | int]:
127140
"authors": ", ".join(author.nick for author in self.authors),
128141
}
129142

143+
def json(self) -> dict[str, str]:
144+
return {
145+
"title": self.title,
146+
"authors": ", ".join(author.nick for author in self.authors),
147+
"discussions_to": self.discussions_to,
148+
"status": self.status,
149+
"type": self.pep_type,
150+
"created": self.created,
151+
"python_version": self.python_version,
152+
"post_history": self.post_history,
153+
"resolution": self.resolution,
154+
"requires": self.requires,
155+
"replaces": self.replaces,
156+
"superseded_by": self.superseded_by,
157+
"url": f"https://peps.python.org/pep-{self.number:0>4}/",
158+
}
159+
130160

131161
def _raise_pep_error(pep: PEP, msg: str, pep_num: bool = False) -> None:
132162
if pep_num:

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,79 +17,52 @@
1717
"""
1818
from __future__ import annotations
1919

20-
import csv
2120
import json
2221
from pathlib import Path
23-
import re
2422
from typing import TYPE_CHECKING
2523

2624
from pep_sphinx_extensions.pep_zero_generator import parser
25+
from pep_sphinx_extensions.pep_zero_generator import subindicies
2726
from pep_sphinx_extensions.pep_zero_generator import writer
2827

2928
if TYPE_CHECKING:
3029
from sphinx.application import Sphinx
3130
from sphinx.environment import BuildEnvironment
3231

3332

34-
def create_pep_json(peps: list[parser.PEP]) -> str:
35-
pep_dict = {
36-
pep.number: {
37-
"title": pep.title,
38-
"authors": ", ".join(pep.authors.nick for pep.authors in pep.authors),
39-
"discussions_to": pep.discussions_to,
40-
"status": pep.status,
41-
"type": pep.pep_type,
42-
"created": pep.created,
43-
"python_version": pep.python_version,
44-
"post_history": pep.post_history,
45-
"resolution": pep.resolution,
46-
"requires": pep.requires,
47-
"replaces": pep.replaces,
48-
"superseded_by": pep.superseded_by,
49-
"url": f"https://peps.python.org/pep-{pep.number:0>4}/",
50-
}
51-
for pep in sorted(peps)
52-
}
53-
return json.dumps(pep_dict, indent=1)
54-
55-
56-
def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
33+
def _parse_peps() -> list[parser.PEP]:
5734
# Read from root directory
5835
path = Path(".")
59-
60-
pep_zero_filename = "pep-0000"
6136
peps: list[parser.PEP] = []
62-
pep_pat = re.compile(r"pep-\d{4}") # Path.match() doesn't support regular expressions
63-
64-
# AUTHOR_OVERRIDES.csv is an exception file for PEP0 name parsing
65-
with open("AUTHOR_OVERRIDES.csv", "r", encoding="utf-8") as f:
66-
authors_overrides = {}
67-
for line in csv.DictReader(f):
68-
full_name = line.pop("Overridden Name")
69-
authors_overrides[full_name] = line
7037

7138
for file_path in path.iterdir():
7239
if not file_path.is_file():
7340
continue # Skip directories etc.
7441
if file_path.match("pep-0000*"):
7542
continue # Skip pre-existing PEP 0 files
76-
if pep_pat.match(str(file_path)) and file_path.suffix in {".txt", ".rst"}:
77-
pep = parser.PEP(path.joinpath(file_path).absolute(), authors_overrides)
43+
if (len(file_path.stem) == 8
44+
and file_path.stem.startswith("pep-")
45+
and file_path.suffix in {".txt", ".rst"}):
46+
pep = parser.PEP(path.joinpath(file_path).absolute())
7847
peps.append(pep)
7948

80-
pep0_text = writer.PEPZeroWriter().write_pep0(sorted(peps))
81-
pep0_path = Path(f"{pep_zero_filename}.rst")
82-
pep0_path.write_text(pep0_text, encoding="utf-8")
49+
return sorted(peps)
50+
51+
52+
def create_pep_json(peps: list[parser.PEP]) -> str:
53+
return json.dumps({pep.number: pep.json() for pep in peps}, indent=1)
54+
55+
56+
def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
57+
peps = _parse_peps()
8358

84-
peps.append(parser.PEP(pep0_path, authors_overrides))
59+
pep0_text = writer.PEPZeroWriter().write_pep0(peps)
60+
pep0_path = subindicies.update_sphinx("pep-0000", pep0_text, docnames, env)
61+
peps.append(parser.PEP(pep0_path))
8562

86-
# Add to files for builder
87-
docnames.insert(1, pep_zero_filename)
88-
# Add to files for writer
89-
env.found_docs.add(pep_zero_filename)
63+
# subindicies_to_generate = ("packaging",)
64+
subindicies_to_generate = ()
65+
subindicies.generate_subindicies(subindicies_to_generate, peps, docnames, env)
9066

9167
# Create peps.json
92-
pep0_json = create_pep_json(peps)
93-
out_dir = Path(app.outdir) / "api"
94-
out_dir.mkdir(exist_ok=True)
95-
Path(out_dir, "peps.json").write_text(pep0_json, encoding="utf-8")
68+
Path(app.outdir, "peps.json").write_text(create_pep_json(peps), encoding="utf-8")
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Utilities to support sub-indices for PEPs."""
2+
3+
from __future__ import annotations
4+
5+
from pathlib import Path
6+
from typing import TYPE_CHECKING
7+
8+
from pep_sphinx_extensions.pep_zero_generator import writer
9+
10+
if TYPE_CHECKING:
11+
from sphinx.environment import BuildEnvironment
12+
13+
from pep_sphinx_extensions.pep_zero_generator.parser import PEP
14+
15+
16+
def update_sphinx(filename: str, text: str, docnames: list[str], env: BuildEnvironment) -> Path:
17+
file_path = Path(f"{filename}.rst").resolve()
18+
file_path.parent.mkdir(parents=True, exist_ok=True)
19+
file_path.write_text(text, encoding="utf-8")
20+
21+
# Add to files for builder
22+
docnames.insert(1, filename)
23+
# Add to files for writer
24+
env.found_docs.add(filename)
25+
26+
return file_path
27+
28+
29+
def generate_subindicies(
30+
subindicies: tuple[str],
31+
peps: list[PEP],
32+
docnames: list[str],
33+
env: BuildEnvironment
34+
) -> None:
35+
for subindex in subindicies:
36+
header_text = f"{subindex.title()} PEPs"
37+
header_line = "#" * len(header_text)
38+
header = header_text + "\n" + header_line + "\n"
39+
40+
topic = subindex.lower()
41+
filtered_peps = [pep for pep in peps if topic in pep.topics]
42+
subindex_intro = f"""\
43+
This is the index of all Python Enhancement Proposals (PEPs) labelled
44+
under the '{subindex.title()}' topic. This is a sub-index of :pep:`0`,
45+
the PEP index.
46+
"""
47+
subindex_text = writer.PEPZeroWriter().write_pep0(
48+
filtered_peps, header, subindex_intro, is_subindex=True,
49+
)
50+
update_sphinx(f"topic/{subindex}", subindex_text, docnames, env)

pep_sphinx_extensions/pep_zero_generator/writer.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
title_length=title_length
3737
)
3838

39-
header = f"""\
39+
HEADER = f"""\
4040
PEP: 0
4141
Title: Index of Python Enhancement Proposals (PEPs)
4242
Last-Modified: {datetime.date.today()}
@@ -47,7 +47,7 @@
4747
Created: 13-Jul-2000
4848
"""
4949

50-
intro = """\
50+
INTRO = """\
5151
This PEP contains the index of all Python Enhancement Proposals,
5252
known as PEPs. PEP numbers are :pep:`assigned <1#pep-editors>`
5353
by the PEP editors, and once assigned are never changed. The
@@ -112,7 +112,7 @@ def emit_pep_category(self, category: str, peps: list[PEP]) -> None:
112112
self.emit_table_separator()
113113
self.emit_newline()
114114

115-
def write_pep0(self, peps: list[PEP]):
115+
def write_pep0(self, peps: list[PEP], header: str = HEADER, intro: str = INTRO, is_subindex: bool = False):
116116

117117
# PEP metadata
118118
self.emit_text(header)
@@ -138,7 +138,8 @@ def write_pep0(self, peps: list[PEP]):
138138
("Abandoned, Withdrawn, and Rejected PEPs", dead),
139139
]
140140
for (category, peps_in_category) in pep_categories:
141-
self.emit_pep_category(category, peps_in_category)
141+
if is_subindex and len(peps_in_category) > 0:
142+
self.emit_pep_category(category, peps_in_category)
142143

143144
self.emit_newline()
144145

@@ -152,13 +153,14 @@ def write_pep0(self, peps: list[PEP]):
152153
self.emit_newline()
153154

154155
# Reserved PEP numbers
155-
self.emit_title("Reserved PEP Numbers")
156-
self.emit_column_headers()
157-
for number, claimants in sorted(self.RESERVED.items()):
158-
self.emit_pep_row({"type": ".", "status": ".", "number": number, "title": "RESERVED", "authors": claimants})
156+
if not is_subindex:
157+
self.emit_title("Reserved PEP Numbers")
158+
self.emit_column_headers()
159+
for number, claimants in sorted(self.RESERVED.items()):
160+
self.emit_pep_row({"type": ".", "status": ".", "number": number, "title": "RESERVED", "authors": claimants})
159161

160-
self.emit_table_separator()
161-
self.emit_newline()
162+
self.emit_table_separator()
163+
self.emit_newline()
162164

163165
# PEP types key
164166
self.emit_title("PEP Types Key")

0 commit comments

Comments
 (0)