Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infra: Prepare for moving PEPs to a subfolder #3417

Merged
merged 5 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 -----------------------------------------------------

Expand Down Expand Up @@ -60,16 +62,18 @@

# -- 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
html_permalinks = False # handled in the PEPContents transform
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`
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from docutils import nodes
from docutils.parsers import rst


PYPA_SPEC_BASE_URL = "https://packaging.python.org/en/latest/specifications/"


Expand Down
40 changes: 21 additions & 19 deletions pep_sphinx_extensions/pep_processor/transforms/pep_footer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import datetime as dt
import time
from pathlib import Path
import subprocess

Expand All @@ -23,7 +23,7 @@

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-????.???"):

Check warning on line 26 in pep_sphinx_extensions/pep_processor/transforms/pep_footer.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_processor/transforms/pep_footer.py#L26

Added line #L26 was not covered by tests
return # not a PEP file, exit early

# Iterate through sections from the end of the document
Expand Down Expand Up @@ -62,42 +62,44 @@
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)


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 {}

Check warning on line 79 in pep_sphinx_extensions/pep_processor/transforms/pep_footer.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_processor/transforms/pep_footer.py#L79

Added line #L79 was not covered by tests
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

Check warning on line 99 in pep_sphinx_extensions/pep_processor/transforms/pep_footer.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_processor/transforms/pep_footer.py#L98-L99

Added lines #L98 - L99 were not covered by tests
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()
2 changes: 1 addition & 1 deletion pep_sphinx_extensions/pep_theme/templates/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ <h1>Python Enhancement Proposals</h1>
<h2>Contents</h2>
{{ toc }}
<br>
{%- if not (sourcename.startswith("pep-0000") or sourcename.startswith("topic")) %}
{%- if not sourcename.startswith(("pep-0000", "topic")) %}
<a id="source" href="https://github.com/python/peps/blob/main/{{sourcename}}">Page Source (GitHub)</a>
{%- endif %}
</nav>
Expand Down
21 changes: 13 additions & 8 deletions pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -52,16 +52,21 @@
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")

Check warning on line 60 in pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py#L57-L60

Added lines #L57 - L60 were not covered by tests


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

Check warning on line 64 in pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py#L64

Added line #L64 was not covered by tests

pep0_text = writer.PEPZeroWriter().write_pep0(peps, builder=env.settings["builder"])
pep0_path = subindices.update_sphinx("pep-0000", pep0_text, docnames, env)
peps.append(parser.PEP(pep0_path))

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))

Check warning on line 72 in pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py#L72

Added line #L72 was not covered by tests
7 changes: 5 additions & 2 deletions pep_sphinx_extensions/pep_zero_generator/subindices.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import os
from pathlib import Path
from typing import TYPE_CHECKING

Expand All @@ -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")

Check warning on line 18 in pep_sphinx_extensions/pep_zero_generator/subindices.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_zero_generator/subindices.py#L18

Added line #L18 was not covered by tests
file_path.write_text(text, encoding="utf-8")

# Add to files for builder
Expand All @@ -32,6 +32,9 @@
docnames: list[str],
env: BuildEnvironment,
) -> None:
# create topic directory
os.makedirs(os.path.join(env.srcdir, "topic"), exist_ok=True)

Check warning on line 36 in pep_sphinx_extensions/pep_zero_generator/subindices.py

View check run for this annotation

Codecov / codecov/patch

pep_sphinx_extensions/pep_zero_generator/subindices.py#L36

Added line #L36 was not covered by tests

# Create sub index page
generate_topic_contents(docnames, env)

Expand Down
6 changes: 2 additions & 4 deletions pep_sphinx_extensions/pep_zero_generator/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions pep_sphinx_extensions/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from pathlib import Path

PEP_ROOT = Path(__file__, "..", "..", "..").resolve()
Original file line number Diff line number Diff line change
@@ -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(
"<paragraph>Last modified: "
Expand All @@ -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) == "<paragraph/>"

Expand All @@ -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
18 changes: 9 additions & 9 deletions pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from pathlib import Path

import pytest

from pep_sphinx_extensions.pep_zero_generator import parser
Expand All @@ -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) == "<PEP 0008 - Style Guide for Python Code>"


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",
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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)

Expand Down