From 3d60b84e3533e32f845224714649dcb566b9d135 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Sun, 9 Jan 2022 18:07:03 +0000 Subject: [PATCH] PEP 676: Implementation updates (#2208) --- .github/workflows/deploy-gh-pages.yaml | 18 ++--- Makefile | 2 +- build.py | 2 - conf.py | 4 +- pep_rss_gen.py => generate_rss.py | 49 +++++--------- pep-0456.txt | 2 +- pep_sphinx_extensions/__init__.py | 23 ++++--- pep_sphinx_extensions/config.py | 6 -- .../pep_processor/html/pep_html_builder.py | 50 ++++++++++++++ .../pep_processor/html/pep_html_translator.py | 44 ++++++++----- .../pep_processor/parsing/pep_role.py | 5 +- .../pep_processor/transforms/pep_footer.py | 46 ++++++++++--- .../pep_processor/transforms/pep_headers.py | 32 ++------- .../pep_processor/transforms/pep_zero.py | 36 ++++++++-- .../pep_theme/static/doctools.js | 5 -- .../pep_theme/static/style.css | 3 + .../pep_theme/templates/page.html | 3 +- .../pep_zero_generator/writer.py | 65 ++++++++----------- 18 files changed, 215 insertions(+), 180 deletions(-) rename pep_rss_gen.py => generate_rss.py (72%) delete mode 100644 pep_sphinx_extensions/config.py create mode 100644 pep_sphinx_extensions/pep_processor/html/pep_html_builder.py delete mode 100644 pep_sphinx_extensions/pep_theme/static/doctools.js diff --git a/.github/workflows/deploy-gh-pages.yaml b/.github/workflows/deploy-gh-pages.yaml index 98720e06c..bb96d063b 100644 --- a/.github/workflows/deploy-gh-pages.yaml +++ b/.github/workflows/deploy-gh-pages.yaml @@ -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) diff --git a/Makefile b/Makefile index 0f201b0c0..080be0268 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/build.py b/build.py index 97c014536..784fbea54 100644 --- a/build.py +++ b/build.py @@ -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: diff --git a/conf.py b/conf.py index 8eb397038..b7b374055 100644 --- a/conf.py +++ b/conf.py @@ -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 # 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` diff --git a/pep_rss_gen.py b/generate_rss.py similarity index 72% rename from pep_rss_gen.py rename to generate_rss.py index a06ffd20c..88798cb79 100644 --- a/pep_rss_gen.py +++ b/generate_rss.py @@ -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" 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) diff --git a/pep-0456.txt b/pep-0456.txt index a9866e780..b7ee2e344 100644 --- a/pep-0456.txt +++ b/pep-0456.txt @@ -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]_. diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 37ffde408..f302b14b7 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -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) diff --git a/pep_sphinx_extensions/config.py b/pep_sphinx_extensions/config.py deleted file mode 100644 index 64f3ad283..000000000 --- a/pep_sphinx_extensions/config.py +++ /dev/null @@ -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/" diff --git a/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py new file mode 100644 index 000000000..703aa3af3 --- /dev/null +++ b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py @@ -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 diff --git a/pep_sphinx_extensions/pep_processor/html/pep_html_translator.py b/pep_sphinx_extensions/pep_processor/html/pep_html_translator.py index 9e87daf89..d1ad27f2f 100644 --- a/pep_sphinx_extensions/pep_processor/html/pep_html_translator.py +++ b/pep_sphinx_extensions/pep_processor/html/pep_html_translator.py @@ -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(']') + + 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'') + self.context.append(f"]") + 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("") - self.body.append("\n
") - return - - # If only one reference to this footnote - back_references = node.parent["backrefs"] - if len(back_references) == 1: - self.body.append("") - - # Close the tag - self.body.append("") - - # If more than one reference - if len(back_references) > 1: - back_links = [f"{i}" for i, ref in enumerate(back_references, start=1)] - back_links_str = ", ".join(back_links) - self.body.append(f" ({back_links_str}) ") + 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"{i}" for i, ref in enumerate(back_refs, start=1)) + self.body.append(f" ({back_links}) ") # Close the def tags self.body.append("\n
") diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py index 6260c3422..e74ab0dd3 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_role.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_role.py @@ -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 diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py index 39dad0404..497811dc4 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py @@ -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() diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_headers.py b/pep_sphinx_extensions/pep_processor/transforms/pep_headers.py index 12805db49..efc1b87c0 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_headers.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_headers.py @@ -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("@", " at "), 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 diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_zero.py b/pep_sphinx_extensions/pep_processor/transforms/pep_zero.py index b638dbbb8..25a2082ab 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_zero.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_zero.py @@ -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("@", " at "), 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 diff --git a/pep_sphinx_extensions/pep_theme/static/doctools.js b/pep_sphinx_extensions/pep_theme/static/doctools.js deleted file mode 100644 index 5676a8ff3..000000000 --- a/pep_sphinx_extensions/pep_theme/static/doctools.js +++ /dev/null @@ -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 + "]") diff --git a/pep_sphinx_extensions/pep_theme/static/style.css b/pep_sphinx_extensions/pep_theme/static/style.css index 29d90b350..a23a9ae60 100644 --- a/pep_sphinx_extensions/pep_theme/static/style.css +++ b/pep_sphinx_extensions/pep_theme/static/style.css @@ -290,3 +290,6 @@ nav#pep-sidebar ul { nav#pep-sidebar ul a { text-decoration: none; } +#source { + padding-bottom: 2rem; +} diff --git a/pep_sphinx_extensions/pep_theme/templates/page.html b/pep_sphinx_extensions/pep_theme/templates/page.html index b1d8725c4..d47644a73 100644 --- a/pep_sphinx_extensions/pep_theme/templates/page.html +++ b/pep_sphinx_extensions/pep_theme/templates/page.html @@ -29,9 +29,8 @@

Contents

{{ toc }}
- Page Source (GitHub) + Page Source (GitHub) - diff --git a/pep_sphinx_extensions/pep_zero_generator/writer.py b/pep_sphinx_extensions/pep_zero_generator/writer.py index 61f98f091..4a8657b33 100644 --- a/pep_sphinx_extensions/pep_zero_generator/writer.py +++ b/pep_sphinx_extensions/pep_zero_generator/writer.py @@ -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 `_ 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