PEP 676: Implementation updates (#2208)

This commit is contained in:
Adam Turner 2022-01-09 18:07:03 +00:00 committed by GitHub
parent 19684a0787
commit 3d60b84e35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 215 additions and 180 deletions

View File

@ -14,26 +14,16 @@ jobs:
with:
fetch-depth: 0 # fetch all history so that last modified date-times are accurate
- name: 🐍 Set up Python 3.9
- name: 🐍 Set up Python 3
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: 🧳 Cache pip
uses: actions/cache@v2
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
python-version: 3
cache: "pip"
- name: 👷‍ Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
python -m pip install -r requirements.txt
- name: 🔧 Build PEPs
run: make pages -j$(nproc)

View File

@ -62,7 +62,7 @@ SPHINX_BUILD=$(PYTHON) build.py -j $(SPHINX_JOBS)
# TODO replace `rss:` with this when merged & tested
pep_rss:
$(PYTHON) pep_rss_gen.py
$(PYTHON) generate_rss.py
pages: pep_rss
$(SPHINX_BUILD) --index-file

View File

@ -70,8 +70,6 @@ if __name__ == "__main__":
warningiserror=args.fail_on_warning,
parallel=args.jobs,
)
app.builder.copysource = False # Prevent unneeded source copying - we link direct to GitHub
app.builder.search = False # Disable search
app.build()
if args.index_file:

View File

@ -43,16 +43,14 @@ exclude_patterns = [
# HTML output settings
html_math_renderer = "maths_to_html" # Maths rendering
html_show_copyright = False # Turn off miscellany
html_show_sphinx = False
html_title = "peps.python.org" # Set <title/>
# Theme settings
html_theme_path = ["pep_sphinx_extensions"]
html_theme = "pep_theme" # The actual theme directory (child of html_theme_path)
html_use_index = False # Disable index (we use PEP 0)
html_sourcelink_suffix = "" # Fix links to GitHub (don't append .txt)
html_style = "" # must be defined here or in theme.conf, but is unused
html_permalinks = False # handled in the PEPContents transform
gettext_auto_build = False # speed-ups
templates_path = ['pep_sphinx_extensions/pep_theme/templates'] # Theme template relative paths from `confdir`

View File

@ -1,13 +1,11 @@
import datetime
import email.utils
from pathlib import Path
import re
from dateutil import parser
import docutils.frontend
import docutils.nodes
import docutils.parsers.rst
import docutils.utils
from docutils import frontend
from docutils import nodes
from docutils import utils
from docutils.parsers import rst
from feedgen import entry
from feedgen import feed
@ -44,37 +42,26 @@ def first_line_starting_with(full_path: Path, text: str) -> str:
def pep_creation(full_path: Path) -> datetime.datetime:
created_str = first_line_starting_with(full_path, "Created:")
# bleh, I was hoping to avoid re but some PEPs editorialize on the Created line
# (note as of Aug 2020 only PEP 102 has additional content on the Created line)
m = re.search(r"(\d+[- ][\w\d]+[- ]\d{2,4})", created_str)
if not m:
# some older ones have an empty line, that's okay, if it's old we ipso facto don't care about it.
# "return None" would make the most sense but datetime objects refuse to compare with that. :-|
return datetime.datetime(1900, 1, 1)
created_str = m.group(1)
try:
return parser.parse(created_str, dayfirst=True)
except (ValueError, OverflowError):
return datetime.datetime(1900, 1, 1)
if full_path.stem == "pep-0102":
# remove additional content on the Created line
created_str = created_str.split(" ", 1)[0]
return datetime.datetime.strptime(created_str, "%d-%b-%Y")
def parse_rst(text: str) -> docutils.nodes.document:
rst_parser = docutils.parsers.rst.Parser()
components = (docutils.parsers.rst.Parser,)
settings = docutils.frontend.OptionParser(components=components).get_default_values()
document = docutils.utils.new_document('<rst-doc>', settings=settings)
rst_parser.parse(text, document)
def parse_rst(text: str) -> nodes.document:
settings = frontend.OptionParser((rst.Parser,)).get_default_values()
document = utils.new_document('<rst-doc>', settings=settings)
rst.Parser().parse(text, document)
return document
def pep_abstract(full_path: Path) -> str:
"""Return the first paragraph of the PEP abstract"""
text = full_path.read_text(encoding="utf-8")
for node in parse_rst(text):
if "<title>Abstract</title>" in str(node):
for child in node:
if child.tagname == "paragraph":
return child.astext().strip().replace("\n", " ")
# TODO replace .traverse with .findall when Sphinx updates to docutils>=0.18.1
for node in parse_rst(text).traverse(nodes.section):
if node.next_node(nodes.title).astext() == "Abstract":
return node.next_node(nodes.paragraph).astext().strip().replace("\n", " ")
return ""
@ -119,7 +106,7 @@ def main():
Newest Python Enhancement Proposals (PEPs) - Information on new
language features, and some meta-information like release
procedure and schedules.
""".replace("\n ", " ").strip()
"""
# Setup feed generator
fg = feed.FeedGenerator()
@ -131,7 +118,7 @@ def main():
fg.title("Newest Python PEPs")
fg.link(href="https://www.python.org/dev/peps")
fg.link(href="https://www.python.org/dev/peps/peps.rss", rel="self")
fg.description(desc)
fg.description(" ".join(desc.split()))
fg.lastBuildDate(datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc))
# Add PEP information (ordered by newest first)

View File

@ -529,7 +529,7 @@ not affect any application code.
The benchmarks were conducted on CPython default branch revision b08868fd5994
and the PEP repository [pep-456-repos]_. All upstream changes were merged
into the pep-456 branch. The "performance" CPU governor was configured and
into the ``pep-456`` branch. The "performance" CPU governor was configured and
almost all programs were stopped so the benchmarks were able to utilize
TurboBoost and the CPU caches as much as possible. The raw benchmark results
of multiple machines and platforms are made available at [benchmarks]_.

View File

@ -4,11 +4,12 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from docutils import nodes
from docutils.parsers.rst import states
from docutils.writers.html5_polyglot import HTMLTranslator
from sphinx.environment import BuildEnvironment
from sphinx.environment import default_settings
from sphinx import environment
from pep_sphinx_extensions import config
from pep_sphinx_extensions.pep_processor.html import pep_html_builder
from pep_sphinx_extensions.pep_processor.html import pep_html_translator
from pep_sphinx_extensions.pep_processor.parsing import pep_parser
from pep_sphinx_extensions.pep_processor.parsing import pep_role
@ -20,17 +21,16 @@ if TYPE_CHECKING:
# Monkeypatch sphinx.environment.default_settings as Sphinx doesn't allow custom settings or Readers
# These settings should go in docutils.conf, but are overridden here for now so as not to affect
# pep2html.py
default_settings |= {
environment.default_settings |= {
"pep_references": True,
"rfc_references": True,
"pep_base_url": "",
"pep_file_url_template": "pep-%04d.html",
"pep_file_url_template": "",
"_disable_config": True, # disable using docutils.conf whilst running both PEP generators
}
# Monkeypatch sphinx.environment.BuildEnvironment.collect_relations, as it takes a long time
# and we don't use the parent/next/prev functionality
BuildEnvironment.collect_relations = lambda self: {}
# TODO replace all inlined PEP and RFC strings with marked-up roles, disable pep_references and rfc_references and remove this monkey-patch
states.Inliner.pep_reference = lambda s, m, _l: [nodes.reference("", m.group(0), refuri=s.document.settings.pep_url.format(int(m.group("pepnum2"))))]
def _depart_maths():
@ -39,14 +39,17 @@ def _depart_maths():
def _update_config_for_builder(app: Sphinx):
if app.builder.name == "dirhtml":
config.pep_url = f"../{config.pep_stem}"
app.env.settings["pep_file_url_template"] = "../pep-%04d"
environment.default_settings["pep_url"] = "../pep-{:0>4}"
def setup(app: Sphinx) -> dict[str, bool]:
"""Initialize Sphinx extension."""
environment.default_settings["pep_url"] = "pep-{:0>4}.html"
# Register plugin logic
app.add_builder(pep_html_builder.FileBuilder, override=True)
app.add_builder(pep_html_builder.DirectoryBuilder, override=True)
app.add_source_parser(pep_parser.PEPParser) # Add PEP transforms
app.add_role("pep", pep_role.PEPRole(), override=True) # Transform PEP references to links
app.set_translator("html", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (html builder)

View File

@ -1,6 +0,0 @@
"""Miscellaneous configuration variables for the PEP Sphinx extensions."""
pep_stem = "pep-{:0>4}"
pep_url = f"{pep_stem}.html"
pep_vcs_url = "https://github.com/python/peps/blob/main/"
pep_commits_url = "https://github.com/python/peps/commits/main/"

View File

@ -0,0 +1,50 @@
from pathlib import Path
from docutils import nodes
from docutils.frontend import OptionParser
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
search = False # Disable search
# Things we don't use but that need to exist:
indexer = None
relations = {}
_script_files = _css_files = []
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)
self.docsettings = _opt_parser.get_default_values()
self.globalcontext = {"docstitle": self.config.html_title, "script_files": [], "css_files": []}
def get_doc_context(self, docname: str, body: str, _metatags: str) -> dict:
"""Collect items for the template context of a page."""
try:
title = self.env.longtitles[docname].astext()
except KeyError:
title = ""
# source filename
file_is_rst = Path(self.env.srcdir, docname + ".rst").exists()
source_name = f"{docname}.rst" if file_is_rst else f"{docname}.txt"
# local table of contents
toc_tree = self.env.tocs[docname].deepcopy()
for node in toc_tree.traverse(nodes.reference):
node["refuri"] = node["anchorname"] or '#' # fix targets
toc = self.render_partial(toc_tree)["fragment"]
return {"title": title, "sourcename": source_name, "toc": toc, "body": body}
class DirectoryBuilder(FileBuilder):
# sync all overwritten things from DirectoryHTMLBuilder
name = DirectoryHTMLBuilder.name
get_target_uri = DirectoryHTMLBuilder.get_target_uri
get_outfilename = DirectoryHTMLBuilder.get_outfilename

View File

@ -57,26 +57,34 @@ class PEPTranslator(html5.HTML5Translator):
"""Add corresponding end tag from `visit_paragraph`."""
self.body.append(self.context.pop())
def visit_footnote_reference(self, node):
self.body.append(self.starttag(node, "a", suffix="[",
CLASS=f"footnote-reference {self.settings.footnote_references}",
href=f"#{node['refid']}"
))
def depart_footnote_reference(self, node):
self.body.append(']</a>')
def visit_label(self, node):
# pass parent node to get id into starttag:
self.body.append(self.starttag(node.parent, "dt", suffix="[", CLASS="label"))
# footnote/citation backrefs:
back_refs = node.parent["backrefs"]
if self.settings.footnote_backlinks and len(back_refs) == 1:
self.body.append(f'<a href="#{back_refs[0]}">')
self.context.append(f"</a>]")
else:
self.context.append("]")
def depart_label(self, node) -> None:
"""PEP link/citation block cleanup with italicised backlinks."""
if not self.settings.footnote_backlinks:
self.body.append("</span>")
self.body.append("</dt>\n<dd>")
return
# If only one reference to this footnote
back_references = node.parent["backrefs"]
if len(back_references) == 1:
self.body.append("</a>")
# Close the tag
self.body.append("</span>")
# If more than one reference
if len(back_references) > 1:
back_links = [f"<a href='#{ref}'>{i}</a>" for i, ref in enumerate(back_references, start=1)]
back_links_str = ", ".join(back_links)
self.body.append(f"<span class='fn-backref''><em> ({back_links_str}) </em></span>")
self.body.append(self.context.pop())
back_refs = node.parent["backrefs"]
if self.settings.footnote_backlinks and len(back_refs) > 1:
back_links = ", ".join(f"<a href='#{ref}'>{i}</a>" for i, ref in enumerate(back_refs, start=1))
self.body.append(f"<em> ({back_links}) </em>")
# Close the def tags
self.body.append("</dt>\n<dd>")

View File

@ -1,15 +1,14 @@
from sphinx import roles
from pep_sphinx_extensions import config
class PEPRole(roles.PEP):
"""Override the :pep: role"""
# TODO override the entire thing (internal should be True)
def build_uri(self) -> str:
"""Get PEP URI from role text."""
pep_str, _, fragment = self.target.partition("#")
pep_base = config.pep_url.format(int(pep_str))
pep_base = self.inliner.document.settings.pep_url.format(int(pep_str))
if fragment:
return f"{pep_base}#{fragment}"
return pep_base

View File

@ -5,8 +5,6 @@ import subprocess
from docutils import nodes
from docutils import transforms
from pep_sphinx_extensions import config
class PEPFooter(transforms.Transform):
"""Footer transforms for PEPs.
@ -49,21 +47,49 @@ class PEPFooter(transforms.Transform):
def _add_source_link(pep_source_path: Path) -> nodes.paragraph:
"""Add link to source text on VCS (GitHub)"""
source_link = config.pep_vcs_url + pep_source_path.name
source_link = f"https://github.com/python/peps/blob/main/{pep_source_path.name}"
link_node = nodes.reference("", source_link, refuri=source_link)
return nodes.paragraph("", "Source: ", link_node)
def _add_commit_history_info(pep_source_path: Path) -> nodes.paragraph:
"""Use local git history to find last modified date."""
args = ["git", "--no-pager", "log", "-1", "--format=%at", pep_source_path.name]
try:
file_modified = subprocess.check_output(args)
since_epoch = file_modified.decode("utf-8").strip()
dt = datetime.datetime.utcfromtimestamp(float(since_epoch))
except (subprocess.CalledProcessError, ValueError):
since_epoch = LAST_MODIFIED_TIMES[pep_source_path.name]
except KeyError:
return nodes.paragraph()
commit_link = config.pep_commits_url + pep_source_path.name
link_node = nodes.reference("", f"{dt.isoformat(sep=' ')} GMT", refuri=commit_link)
iso_time = datetime.datetime.utcfromtimestamp(since_epoch).isoformat(sep=" ")
commit_link = f"https://github.com/python/peps/commits/main/{pep_source_path.name}"
link_node = nodes.reference("", f"{iso_time} GMT", refuri=commit_link)
return nodes.paragraph("", "Last modified: ", link_node)
def _get_last_modified_timestamps():
# get timestamps and changed files from all commits (without paging results)
args = ["git", "--no-pager", "log", "--format=#%at", "--name-only"]
with subprocess.Popen(args, stdout=subprocess.PIPE) as process:
all_modified = process.stdout.read().decode("utf-8")
process.stdout.close()
if process.wait(): # non-zero return code
return {}
# set up the dictionary with the *current* files
last_modified = {path.name: 0 for path in Path().glob("pep-*") if path.suffix in {".txt", ".rst"}}
# iterate through newest to oldest, updating per file timestamps
change_sets = all_modified.removeprefix("#").split("#")
for change_set in change_sets:
timestamp, files = change_set.split("\n", 1)
for file in files.strip().split("\n"):
if file.startswith("pep-") and file[-3:] in {"txt", "rst"}:
if last_modified.get(file) == 0:
try:
last_modified[file] = float(timestamp)
except ValueError:
pass # if float conversion fails
return last_modified
LAST_MODIFIED_TIMES = _get_last_modified_timestamps()

View File

@ -1,5 +1,3 @@
from __future__ import annotations
from pathlib import Path
import re
@ -7,8 +5,8 @@ from docutils import nodes
from docutils import transforms
from sphinx import errors
from pep_sphinx_extensions import config
from pep_sphinx_extensions.pep_processor.transforms import pep_zero
from pep_sphinx_extensions.pep_processor.transforms.pep_zero import _mask_email
class PEPParsingError(errors.SphinxError):
@ -79,9 +77,9 @@ class PEPHeaders(transforms.Transform):
elif name in {"replaces", "superseded-by", "requires"}:
# replace PEP numbers with normalised list of links to PEPs
new_body = []
for ref_pep in re.split(r",?\s+", body.astext()):
new_body += [nodes.reference("", ref_pep, refuri=config.pep_url.format(int(ref_pep)))]
new_body += [nodes.Text(", ")]
for pep_str in re.split(r",?\s+", body.astext()):
target = self.document.settings.pep_url.format(int(pep_str))
new_body += [nodes.reference("", pep_str, refuri=target), nodes.Text(", ")]
para[:] = new_body[:-1] # drop trailing space
elif name in {"last-modified", "content-type", "version"}:
# Mark unneeded fields
@ -90,25 +88,3 @@ class PEPHeaders(transforms.Transform):
# Remove unneeded fields
for field in fields_to_remove:
field.parent.remove(field)
def _mask_email(ref: nodes.reference, pep_num: int | None = None) -> nodes.reference:
"""Mask the email address in `ref` and return a replacement node.
`ref` is returned unchanged if it contains no email address.
If given an email not explicitly whitelisted, process it such that
`user@host` -> `user at host`.
If given a PEP number `pep_num`, add a default email subject.
"""
if "refuri" not in ref or not ref["refuri"].startswith("mailto:"):
return ref
non_masked_addresses = {"peps@python.org", "python-list@python.org", "python-dev@python.org"}
if ref["refuri"].removeprefix("mailto:").strip() not in non_masked_addresses:
ref[0] = nodes.raw("", ref[0].replace("@", "&#32;&#97;t&#32;"), format="html")
if pep_num is None:
return ref[0] # return email text without mailto link
ref["refuri"] += f"?subject=PEP%20{pep_num}"
return ref

View File

@ -1,8 +1,7 @@
from __future__ import annotations
from docutils import nodes
from docutils import transforms
from docutils.transforms import peps
from pep_sphinx_extensions import config
class PEPZero(transforms.Transform):
@ -38,7 +37,7 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
@staticmethod
def visit_reference(node: nodes.reference) -> None:
"""Mask email addresses if present."""
node.replace_self(peps.mask_email(node))
node.replace_self(_mask_email(node))
@staticmethod
def visit_field_list(node: nodes.field_list) -> None:
@ -68,7 +67,30 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
if isinstance(para, nodes.paragraph) and len(para) == 1:
pep_str = para.astext()
try:
ref = config.pep_url.format(int(pep_str))
para[0] = nodes.reference(pep_str, pep_str, refuri=ref)
pep_num = int(pep_str)
except ValueError:
pass
return
ref = self.document.settings.pep_url.format(pep_num)
para[0] = nodes.reference("", pep_str, refuri=ref)
def _mask_email(ref: nodes.reference, pep_num: int | None = None) -> nodes.reference:
"""Mask the email address in `ref` and return a replacement node.
`ref` is returned unchanged if it contains no email address.
If given an email not explicitly whitelisted, process it such that
`user@host` -> `user at host`.
If given a PEP number `pep_num`, add a default email subject.
"""
if "refuri" not in ref or not ref["refuri"].startswith("mailto:"):
return ref
non_masked_addresses = {"peps@python.org", "python-list@python.org", "python-dev@python.org"}
if ref["refuri"].removeprefix("mailto:").strip() not in non_masked_addresses:
ref[0] = nodes.raw("", ref[0].replace("@", "&#32;&#97;t&#32;"), format="html")
if pep_num is None:
return ref[0] # return email text without mailto link
ref["refuri"] += f"?subject=PEP%20{pep_num}"
return ref

View File

@ -1,5 +0,0 @@
/* JavaScript utilities for all documentation. */
// Footnote fixer
document.querySelectorAll("span.brackets").forEach(el => el.innerHTML = "[" + el.innerHTML + "]")
document.querySelectorAll("a.brackets").forEach(el => el.innerHTML = "[" + el.innerHTML + "]")

View File

@ -290,3 +290,6 @@ nav#pep-sidebar ul {
nav#pep-sidebar ul a {
text-decoration: none;
}
#source {
padding-bottom: 2rem;
}

View File

@ -29,9 +29,8 @@
<h2>Contents</h2>
{{ toc }}
<br />
<strong><a href="https://github.com/python/peps/blob/main/{{sourcename}}">Page Source (GitHub)</a></strong>
<strong id="source"><a href="https://github.com/python/peps/blob/main/{{sourcename}}">Page Source (GitHub)</a></strong>
</nav>
</section>
<script src="{{ pathto('_static/doctools.js', resource=True) }}"></script>
</body>
</html>

View File

@ -49,16 +49,12 @@ Created: 13-Jul-2000
intro = """\
This PEP contains the index of all Python Enhancement Proposals,
known as PEPs. PEP numbers are assigned by the PEP editors, and
once assigned are never changed [1_]. The version control history [2_] of
known as PEPs. PEP numbers are :pep:`assigned <1#pep-editors>`
by the PEP editors, and once assigned are never changed. The
`version control history <https://github.com/python/peps>`_ of
the PEP texts represent their historical record.
"""
references = """\
.. [1] PEP 1: PEP Purpose and Guidelines
.. [2] View PEP history online: https://github.com/python/peps
"""
class PEPZeroWriter:
# This is a list of reserved PEP numbers. Reservations are not to be used for
@ -100,17 +96,16 @@ class PEPZeroWriter:
self.emit_pep_row({"status": ".", "type": ".", "number": "PEP", "title": "PEP Title", "authors": "PEP Author(s)"})
self.emit_table_separator()
def emit_title(self, text: str, anchor: str, *, symbol: str = "=") -> None:
self.output.append(f".. _{anchor}:\n")
def emit_title(self, text: str, *, symbol: str = "=") -> None:
self.output.append(text)
self.output.append(symbol * len(text))
self.emit_newline()
def emit_subtitle(self, text: str, anchor: str) -> None:
self.emit_title(text, anchor, symbol="-")
def emit_subtitle(self, text: str) -> None:
self.emit_title(text, symbol="-")
def emit_pep_category(self, category: str, anchor: str, peps: list[PEP]) -> None:
self.emit_subtitle(category, anchor)
def emit_pep_category(self, category: str, peps: list[PEP]) -> None:
self.emit_subtitle(category)
self.emit_column_headers()
for pep in peps:
self.output.append(column_format(**pep.details(title_length=title_length)))
@ -124,44 +119,40 @@ class PEPZeroWriter:
self.emit_newline()
# Introduction
self.emit_title("Introduction", "intro")
self.emit_title("Introduction")
self.emit_text(intro)
self.emit_newline()
# PEPs by category
self.emit_title("Index by Category", "by-category")
self.emit_title("Index by Category")
meta, info, provisional, accepted, open_, finished, historical, deferred, dead = _classify_peps(peps)
pep_categories = [
("Meta-PEPs (PEPs about PEPs or Processes)", "by-category-meta", meta),
("Other Informational PEPs", "by-category-other-info", info),
("Provisional PEPs (provisionally accepted; interface may still change)", "by-category-provisional", provisional),
("Accepted PEPs (accepted; may not be implemented yet)", "by-category-accepted", accepted),
("Open PEPs (under consideration)", "by-category-open", open_),
("Finished PEPs (done, with a stable interface)", "by-category-finished", finished),
("Historical Meta-PEPs and Informational PEPs", "by-category-historical", historical),
("Deferred PEPs (postponed pending further research or updates)", "by-category-deferred", deferred),
("Abandoned, Withdrawn, and Rejected PEPs", "by-category-abandoned", dead),
("Meta-PEPs (PEPs about PEPs or Processes)", meta),
("Other Informational PEPs", info),
("Provisional PEPs (provisionally accepted; interface may still change)", provisional),
("Accepted PEPs (accepted; may not be implemented yet)", accepted),
("Open PEPs (under consideration)", open_),
("Finished PEPs (done, with a stable interface)", finished),
("Historical Meta-PEPs and Informational PEPs", historical),
("Deferred PEPs (postponed pending further research or updates)", deferred),
("Abandoned, Withdrawn, and Rejected PEPs", dead),
]
for (category, anchor, peps_in_category) in pep_categories:
self.emit_pep_category(category, anchor, peps_in_category)
for (category, peps_in_category) in pep_categories:
self.emit_pep_category(category, peps_in_category)
self.emit_newline()
# PEPs by number
self.emit_title("Numerical Index", "by-pep-number")
self.emit_title("Numerical Index")
self.emit_column_headers()
prev_pep = 0
for pep in peps:
if pep.number - prev_pep > 1:
self.emit_newline()
self.emit_pep_row(pep.details(title_length=title_length))
prev_pep = pep.number
self.emit_table_separator()
self.emit_newline()
# Reserved PEP numbers
self.emit_title("Reserved PEP Numbers", "reserved")
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})
@ -170,7 +161,7 @@ class PEPZeroWriter:
self.emit_newline()
# PEP types key
self.emit_title("PEP Types Key", "type-key")
self.emit_title("PEP Types Key")
for type_ in sorted(TYPE_VALUES):
self.emit_text(f" {type_[0]} - {type_} PEP")
self.emit_newline()
@ -178,7 +169,7 @@ class PEPZeroWriter:
self.emit_newline()
# PEP status key
self.emit_title("PEP Status Key", "status-key")
self.emit_title("PEP Status Key")
for status in sorted(STATUS_VALUES):
# Draft PEPs have no status displayed, Active shares a key with Accepted
if status in HIDE_STATUS:
@ -195,7 +186,7 @@ class PEPZeroWriter:
# PEP owners
authors_dict = _verify_email_addresses(peps)
max_name_len = max(len(author_name) for author_name in authors_dict)
self.emit_title("Authors/Owners", "authors")
self.emit_title("Authors/Owners")
self.emit_author_table_separator(max_name_len)
self.emit_text(f"{'Name':{max_name_len}} Email Address")
self.emit_author_table_separator(max_name_len)
@ -207,10 +198,6 @@ class PEPZeroWriter:
self.emit_newline()
self.emit_newline()
# References for introduction footnotes
self.emit_title("References", "references")
self.emit_text(references)
pep0_string = "\n".join([str(s) for s in self.output])
return pep0_string