diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 4602dbe7b38..5b02ed79186 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -12,7 +12,6 @@ requirements.txt @AA-Turner
infra/ @ewdurbin
pep_sphinx_extensions/ @AA-Turner
-AUTHOR_OVERRIDES.csv @AA-Turner
build.py @AA-Turner
conf.py @AA-Turner
contents.rst @AA-Turner
diff --git a/conf.py b/conf.py
index 95a1debd451..b795aa874c9 100644
--- a/conf.py
+++ b/conf.py
@@ -3,10 +3,12 @@
"""Configuration for building PEPs using Sphinx."""
+import os
from pathlib import Path
import sys
-sys.path.append(str(Path(".").absolute()))
+_ROOT = Path(__file__).resolve().parent
+sys.path.append(os.fspath(_ROOT))
# -- Project information -----------------------------------------------------
@@ -60,11 +62,13 @@
# -- Options for HTML output -------------------------------------------------
+_PSE_PATH = _ROOT / "pep_sphinx_extensions"
+
# HTML output settings
html_math_renderer = "maths_to_html" # Maths rendering
# Theme settings
-html_theme_path = ["pep_sphinx_extensions"]
+html_theme_path = [os.fspath(_PSE_PATH)]
html_theme = "pep_theme" # The actual theme directory (child of html_theme_path)
html_use_index = False # Disable index (we use PEP 0)
html_style = "" # must be defined here or in theme.conf, but is unused
@@ -72,4 +76,4 @@
html_baseurl = "https://peps.python.org" # to create the CNAME file
gettext_auto_build = False # speed-ups
-templates_path = ["pep_sphinx_extensions/pep_theme/templates"] # Theme template relative paths from `confdir`
+templates_path = [os.fspath(_PSE_PATH / "pep_theme" / "templates")] # Theme template relative paths from `confdir`
diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py
index f3a55270cd7..d0f1cbee209 100644
--- a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py
+++ b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py
@@ -5,7 +5,6 @@
from docutils import nodes
from docutils.parsers import rst
-
PYPA_SPEC_BASE_URL = "https://packaging.python.org/en/latest/specifications/"
diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py
index 7b9c29d5137..efd94ca3625 100644
--- a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py
+++ b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py
@@ -1,4 +1,4 @@
-import datetime as dt
+import time
from pathlib import Path
import subprocess
@@ -23,7 +23,7 @@ class PEPFooter(transforms.Transform):
def apply(self) -> None:
pep_source_path = Path(self.document["source"])
- if not pep_source_path.match("pep-*"):
+ if not pep_source_path.match("pep-????.???"):
return # not a PEP file, exit early
# Iterate through sections from the end of the document
@@ -62,12 +62,10 @@ def _add_source_link(pep_source_path: Path) -> nodes.paragraph:
def _add_commit_history_info(pep_source_path: Path) -> nodes.paragraph:
"""Use local git history to find last modified date."""
try:
- since_epoch = LAST_MODIFIED_TIMES[pep_source_path.name]
+ iso_time = _LAST_MODIFIED_TIMES[pep_source_path.stem]
except KeyError:
return nodes.paragraph()
- epoch_dt = dt.datetime.fromtimestamp(since_epoch, dt.timezone.utc)
- iso_time = epoch_dt.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)
@@ -75,29 +73,33 @@ def _add_commit_history_info(pep_source_path: Path) -> nodes.paragraph:
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 {}
+ args = ("git", "--no-pager", "log", "--format=#%at", "--name-only")
+ ret = subprocess.run(args, stdout=subprocess.PIPE, text=True, encoding="utf-8")
+ if ret.returncode: # non-zero return code
+ return {}
+ all_modified = ret.stdout
# 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"}}
+ peps_dir = Path(__file__, "..", "..", "..", "..").resolve()
+ last_modified = {path.stem: "" for path in peps_dir.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
+ if not file.startswith("pep-") or not file.endswith((".rst", ".txt")):
+ continue # not a PEP
+ file = file[:-4]
+ if last_modified.get(file) != "":
+ continue # most recent modified date already found
+ try:
+ y, m, d, hh, mm, ss, *_ = time.gmtime(float(timestamp))
+ except ValueError:
+ continue # if float conversion fails
+ last_modified[file] = f"{y:04}-{m:02}-{d:02} {hh:02}:{mm:02}:{ss:02}"
return last_modified
-LAST_MODIFIED_TIMES = _get_last_modified_timestamps()
+_LAST_MODIFIED_TIMES = _get_last_modified_timestamps()
diff --git a/pep_sphinx_extensions/pep_theme/templates/page.html b/pep_sphinx_extensions/pep_theme/templates/page.html
index 2831fce0cde..8bd879c0fcc 100644
--- a/pep_sphinx_extensions/pep_theme/templates/page.html
+++ b/pep_sphinx_extensions/pep_theme/templates/page.html
@@ -43,7 +43,7 @@
Python Enhancement Proposals
Contents
{{ toc }}
- {%- if not (sourcename.startswith("pep-0000") or sourcename.startswith("topic")) %}
+ {%- if not sourcename.startswith(("pep-0000", "topic")) %}
Page Source (GitHub)
{%- endif %}
diff --git a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py
index e2c1a7963b7..0804b4aa657 100644
--- a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py
+++ b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py
@@ -18,22 +18,22 @@
from __future__ import annotations
import json
+import os
from pathlib import Path
from typing import TYPE_CHECKING
-from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC
from pep_sphinx_extensions.pep_zero_generator import parser
from pep_sphinx_extensions.pep_zero_generator import subindices
from pep_sphinx_extensions.pep_zero_generator import writer
+from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC
if TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
-def _parse_peps() -> list[parser.PEP]:
+def _parse_peps(path: Path) -> list[parser.PEP]:
# Read from root directory
- path = Path(".")
peps: list[parser.PEP] = []
for file_path in path.iterdir():
@@ -52,8 +52,16 @@ def create_pep_json(peps: list[parser.PEP]) -> str:
return json.dumps({pep.number: pep.full_details for pep in peps}, indent=1)
+def write_peps_json(peps: list[parser.PEP], path: Path) -> None:
+ # Create peps.json
+ json_peps = create_pep_json(peps)
+ Path(path, "peps.json").write_text(json_peps, encoding="utf-8")
+ os.makedirs(os.path.join(path, "api"), exist_ok=True)
+ Path(path, "api", "peps.json").write_text(json_peps, encoding="utf-8")
+
+
def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
- peps = _parse_peps()
+ peps = _parse_peps(Path(app.srcdir))
pep0_text = writer.PEPZeroWriter().write_pep0(peps, builder=env.settings["builder"])
pep0_path = subindices.update_sphinx("pep-0000", pep0_text, docnames, env)
@@ -61,7 +69,4 @@ def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) ->
subindices.generate_subindices(SUBINDICES_BY_TOPIC, peps, docnames, env)
- # Create peps.json
- json_path = Path(app.outdir, "api", "peps.json").resolve()
- json_path.parent.mkdir(exist_ok=True)
- json_path.write_text(create_pep_json(peps), encoding="utf-8")
+ write_peps_json(peps, Path(app.outdir))
diff --git a/pep_sphinx_extensions/pep_zero_generator/subindices.py b/pep_sphinx_extensions/pep_zero_generator/subindices.py
index 455a49dd8f0..3f61b3dd4a9 100644
--- a/pep_sphinx_extensions/pep_zero_generator/subindices.py
+++ b/pep_sphinx_extensions/pep_zero_generator/subindices.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+import os
from pathlib import Path
from typing import TYPE_CHECKING
@@ -14,8 +15,7 @@
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 = Path(env.srcdir, f"{filename}.rst")
file_path.write_text(text, encoding="utf-8")
# Add to files for builder
@@ -32,6 +32,9 @@ def generate_subindices(
docnames: list[str],
env: BuildEnvironment,
) -> None:
+ # create topic directory
+ os.makedirs(os.path.join(env.srcdir, "topic"), exist_ok=True)
+
# Create sub index page
generate_topic_contents(docnames, env)
diff --git a/pep_sphinx_extensions/pep_zero_generator/writer.py b/pep_sphinx_extensions/pep_zero_generator/writer.py
index 02af0c8bdc9..69a5fe4bc6f 100644
--- a/pep_sphinx_extensions/pep_zero_generator/writer.py
+++ b/pep_sphinx_extensions/pep_zero_generator/writer.py
@@ -5,10 +5,8 @@
from typing import TYPE_CHECKING
import unicodedata
-from pep_sphinx_extensions.pep_processor.transforms.pep_headers import (
- ABBREVIATED_STATUSES,
- ABBREVIATED_TYPES,
-)
+from pep_sphinx_extensions.pep_processor.transforms.pep_headers import ABBREVIATED_STATUSES
+from pep_sphinx_extensions.pep_processor.transforms.pep_headers import ABBREVIATED_TYPES
from pep_sphinx_extensions.pep_zero_generator.constants import DEAD_STATUSES
from pep_sphinx_extensions.pep_zero_generator.constants import STATUS_ACCEPTED
from pep_sphinx_extensions.pep_zero_generator.constants import STATUS_ACTIVE
diff --git a/pep_sphinx_extensions/tests/conftest.py b/pep_sphinx_extensions/tests/conftest.py
new file mode 100644
index 00000000000..e1417e08ea3
--- /dev/null
+++ b/pep_sphinx_extensions/tests/conftest.py
@@ -0,0 +1,3 @@
+from pathlib import Path
+
+PEP_ROOT = Path(__file__, "..", "..", "..").resolve()
diff --git a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py
index ad8cf278227..6bd0cdca91a 100644
--- a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py
+++ b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py
@@ -1,16 +1,18 @@
-from pathlib import Path
+import datetime as dt
from pep_sphinx_extensions.pep_processor.transforms import pep_footer
+from ...conftest import PEP_ROOT
+
def test_add_source_link():
- out = pep_footer._add_source_link(Path("pep-0008.txt"))
+ out = pep_footer._add_source_link(PEP_ROOT / "pep-0008.txt")
assert "https://github.com/python/peps/blob/main/pep-0008.txt" in str(out)
def test_add_commit_history_info():
- out = pep_footer._add_commit_history_info(Path("pep-0008.txt"))
+ out = pep_footer._add_commit_history_info(PEP_ROOT / "pep-0008.txt")
assert str(out).startswith(
"Last modified: "
@@ -21,7 +23,7 @@ def test_add_commit_history_info():
def test_add_commit_history_info_invalid():
- out = pep_footer._add_commit_history_info(Path("pep-not-found.txt"))
+ out = pep_footer._add_commit_history_info(PEP_ROOT / "pep-not-found.rst")
assert str(out) == ""
@@ -31,4 +33,4 @@ def test_get_last_modified_timestamps():
assert len(out) >= 585
# Should be a Unix timestamp and at least this
- assert out["pep-0008.txt"] >= 1643124055
+ assert dt.datetime.fromisoformat(out["pep-0008"]).timestamp() >= 1643124055
diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py
index 2cba74df100..cea1d61ab0a 100644
--- a/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py
+++ b/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py
@@ -1,5 +1,3 @@
-from pathlib import Path
-
import pytest
from pep_sphinx_extensions.pep_zero_generator import parser
@@ -19,29 +17,31 @@
)
from pep_sphinx_extensions.pep_zero_generator.parser import _Author
+from ..conftest import PEP_ROOT
+
def test_pep_repr():
- pep8 = parser.PEP(Path("pep-0008.txt"))
+ pep8 = parser.PEP(PEP_ROOT / "pep-0008.txt")
assert repr(pep8) == ""
def test_pep_less_than():
- pep8 = parser.PEP(Path("pep-0008.txt"))
- pep3333 = parser.PEP(Path("pep-3333.txt"))
+ pep8 = parser.PEP(PEP_ROOT / "pep-0008.txt")
+ pep3333 = parser.PEP(PEP_ROOT / "pep-3333.txt")
assert pep8 < pep3333
def test_pep_equal():
- pep_a = parser.PEP(Path("pep-0008.txt"))
- pep_b = parser.PEP(Path("pep-0008.txt"))
+ pep_a = parser.PEP(PEP_ROOT / "pep-0008.txt")
+ pep_b = parser.PEP(PEP_ROOT / "pep-0008.txt")
assert pep_a == pep_b
def test_pep_details(monkeypatch):
- pep8 = parser.PEP(Path("pep-0008.txt"))
+ pep8 = parser.PEP(PEP_ROOT / "pep-0008.txt")
assert pep8.details == {
"authors": "Guido van Rossum, Barry Warsaw, Nick Coghlan",
@@ -106,7 +106,7 @@ def test_parse_authors_invalid():
)
def test_abbreviate_type_status(test_type, test_status, expected):
# set up dummy PEP object and monkeypatch attributes
- pep = parser.PEP(Path("pep-0008.txt"))
+ pep = parser.PEP(PEP_ROOT / "pep-0008.txt")
pep.pep_type = test_type
pep.status = test_status
diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py
index c2e15844fe4..e920d97734a 100644
--- a/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py
+++ b/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py
@@ -1,10 +1,10 @@
-from pathlib import Path
-
from pep_sphinx_extensions.pep_zero_generator import parser, pep_index_generator
+from ..conftest import PEP_ROOT
+
def test_create_pep_json():
- peps = [parser.PEP(Path("pep-0008.txt"))]
+ peps = [parser.PEP(PEP_ROOT / "pep-0008.txt")]
out = pep_index_generator.create_pep_json(peps)