Skip to content

Commit

Permalink
Support subindicies
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed May 5, 2022
1 parent 5232fda commit ff4dd8d
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 61 deletions.
34 changes: 32 additions & 2 deletions pep_sphinx_extensions/pep_zero_generator/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import csv
from email.parser import HeaderParser
from pathlib import Path
import re
Expand All @@ -23,6 +24,14 @@
from pep_sphinx_extensions.pep_zero_generator.author import Author


# AUTHOR_OVERRIDES.csv is an exception file for PEP0 name parsing
AUTHOR_OVERRIDES: dict[str, dict[str, str]] = {}
with open("AUTHOR_OVERRIDES.csv", "r", encoding="utf-8") as f:
for line in csv.DictReader(f):
full_name = line.pop("Overridden Name")
AUTHOR_OVERRIDES[full_name] = line


class PEP:
"""Representation of PEPs.
Expand All @@ -38,7 +47,7 @@ class PEP:
# The required RFC 822 headers for all PEPs.
required_headers = {"PEP", "Title", "Author", "Status", "Type", "Created"}

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

# Parse PEP authors
self.authors: list[Author] = _parse_authors(self, metadata["Author"], authors_overrides)
self.authors: list[Author] = _parse_authors(self, metadata["Author"], AUTHOR_OVERRIDES)

# Topic (for sub-indicies)
_topic = metadata.get("Topic", "").lower().split(",")
self.topics: set[str] = {topic for topic_raw in _topic if (topic := topic_raw.strip())}

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

def json(self) -> dict[str, str]:
return {
"title": self.title,
"authors": ", ".join(author.nick for author in self.authors),
"discussions_to": self.discussions_to,
"status": self.status,
"type": self.pep_type,
"created": self.created,
"python_version": self.python_version,
"post_history": self.post_history,
"resolution": self.resolution,
"requires": self.requires,
"replaces": self.replaces,
"superseded_by": self.superseded_by,
"url": f"https://peps.python.org/pep-{self.number:0>4}/",
}


def _raise_pep_error(pep: PEP, msg: str, pep_num: bool = False) -> None:
if pep_num:
Expand Down
71 changes: 22 additions & 49 deletions pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,79 +17,52 @@
"""
from __future__ import annotations

import csv
import json
from pathlib import Path
import re
from typing import TYPE_CHECKING

from pep_sphinx_extensions.pep_zero_generator import parser
from pep_sphinx_extensions.pep_zero_generator import subindicies
from pep_sphinx_extensions.pep_zero_generator import writer

if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment


def create_pep_json(peps: list[parser.PEP]) -> str:
pep_dict = {
pep.number: {
"title": pep.title,
"authors": ", ".join(pep.authors.nick for pep.authors in pep.authors),
"discussions_to": pep.discussions_to,
"status": pep.status,
"type": pep.pep_type,
"created": pep.created,
"python_version": pep.python_version,
"post_history": pep.post_history,
"resolution": pep.resolution,
"requires": pep.requires,
"replaces": pep.replaces,
"superseded_by": pep.superseded_by,
"url": f"https://peps.python.org/pep-{pep.number:0>4}/",
}
for pep in sorted(peps)
}
return json.dumps(pep_dict, indent=1)


def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
def _parse_peps() -> list[parser.PEP]:
# Read from root directory
path = Path(".")

pep_zero_filename = "pep-0000"
peps: list[parser.PEP] = []
pep_pat = re.compile(r"pep-\d{4}") # Path.match() doesn't support regular expressions

# AUTHOR_OVERRIDES.csv is an exception file for PEP0 name parsing
with open("AUTHOR_OVERRIDES.csv", "r", encoding="utf-8") as f:
authors_overrides = {}
for line in csv.DictReader(f):
full_name = line.pop("Overridden Name")
authors_overrides[full_name] = line

for file_path in path.iterdir():
if not file_path.is_file():
continue # Skip directories etc.
if file_path.match("pep-0000*"):
continue # Skip pre-existing PEP 0 files
if pep_pat.match(str(file_path)) and file_path.suffix in {".txt", ".rst"}:
pep = parser.PEP(path.joinpath(file_path).absolute(), authors_overrides)
if (len(file_path.stem) == 8
and file_path.stem.startswith("pep-")
and file_path.suffix in {".txt", ".rst"}):
pep = parser.PEP(path.joinpath(file_path).absolute())
peps.append(pep)

pep0_text = writer.PEPZeroWriter().write_pep0(sorted(peps))
pep0_path = Path(f"{pep_zero_filename}.rst")
pep0_path.write_text(pep0_text, encoding="utf-8")
return sorted(peps)


def create_pep_json(peps: list[parser.PEP]) -> str:
return json.dumps({pep.number: pep.json() for pep in peps}, indent=1)


def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
peps = _parse_peps()

peps.append(parser.PEP(pep0_path, authors_overrides))
pep0_text = writer.PEPZeroWriter().write_pep0(peps)
pep0_path = subindicies.update_sphinx("pep-0000", pep0_text, docnames, env)
peps.append(parser.PEP(pep0_path))

# Add to files for builder
docnames.insert(1, pep_zero_filename)
# Add to files for writer
env.found_docs.add(pep_zero_filename)
# subindicies_to_generate = ("packaging",)
subindicies_to_generate = ()
subindicies.generate_subindicies(subindicies_to_generate, peps, docnames, env)

# Create peps.json
pep0_json = create_pep_json(peps)
out_dir = Path(app.outdir) / "api"
out_dir.mkdir(exist_ok=True)
Path(out_dir, "peps.json").write_text(pep0_json, encoding="utf-8")
Path(app.outdir, "peps.json").write_text(create_pep_json(peps), encoding="utf-8")
50 changes: 50 additions & 0 deletions pep_sphinx_extensions/pep_zero_generator/subindicies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Utilities to support sub-indices for PEPs."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from pep_sphinx_extensions.pep_zero_generator import writer

if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment

from pep_sphinx_extensions.pep_zero_generator.parser import PEP


def update_sphinx(filename: str, text: str, docnames: list[str], env: BuildEnvironment) -> Path:
file_path = Path(f"{filename}.rst").resolve()
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(text, encoding="utf-8")

# Add to files for builder
docnames.insert(1, filename)
# Add to files for writer
env.found_docs.add(filename)

return file_path


def generate_subindicies(
subindicies: tuple[str],
peps: list[PEP],
docnames: list[str],
env: BuildEnvironment
) -> None:
for subindex in subindicies:
header_text = f"{subindex.title()} PEPs"
header_line = "#" * len(header_text)
header = header_text + "\n" + header_line + "\n"

topic = subindex.lower()
filtered_peps = [pep for pep in peps if topic in pep.topics]
subindex_intro = f"""\
This is the index of all Python Enhancement Proposals (PEPs) labelled
under the '{subindex.title()}' topic. This is a sub-index of :pep:`0`,
the PEP index.
"""
subindex_text = writer.PEPZeroWriter().write_pep0(
filtered_peps, header, subindex_intro, is_subindex=True,
)
update_sphinx(f"topic/{subindex}", subindex_text, docnames, env)
22 changes: 12 additions & 10 deletions pep_sphinx_extensions/pep_zero_generator/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
title_length=title_length
)

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

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

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

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

self.emit_newline()

Expand All @@ -152,13 +153,14 @@ def write_pep0(self, peps: list[PEP]):
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})
if not is_subindex:
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_table_separator()
self.emit_newline()
self.emit_table_separator()
self.emit_newline()

# PEP types key
self.emit_title("PEP Types Key")
Expand Down

0 comments on commit ff4dd8d

Please sign in to comment.