-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathpep_footer.py
108 lines (85 loc) · 4.28 KB
/
pep_footer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import time
from pathlib import Path
import subprocess
from docutils import nodes
from docutils import transforms
class PEPFooter(transforms.Transform):
"""Footer transforms for PEPs.
- Remove the References/Footnotes section if it is empty when rendered.
- Create a link to the (GitHub) source text.
Source Link:
Create the link to the source file from the document source path,
and append the text to the end of the document.
"""
# Uses same priority as docutils.transforms.TargetNotes
default_priority = 520
def apply(self) -> None:
pep_source_path = Path(self.document["source"])
if not pep_source_path.match("pep-????.???"):
return # not a PEP file, exit early
# Iterate through sections from the end of the document
for section in reversed(self.document[0]):
if not isinstance(section, nodes.section):
continue
title_words = {*section[0].astext().lower().split()}
if {"references", "footnotes"} & title_words:
# Remove references/footnotes sections if there is no displayed
# content (i.e. they only have title & link target nodes)
to_hoist = []
types = set()
for node in section:
types.add(type(node))
if isinstance(node, nodes.target):
to_hoist.append(node)
if types <= {nodes.title, nodes.target, nodes.system_message}:
section.parent.extend(to_hoist)
section.parent.remove(section)
# Add link to source text and last modified date
if pep_source_path.stem != "pep-0000":
if pep_source_path.stem != "pep-0210": # 210 is entirely empty, skip
self.document += nodes.transition()
self.document += _add_source_link(pep_source_path)
self.document += _add_commit_history_info(pep_source_path)
def _add_source_link(pep_source_path: Path) -> nodes.paragraph:
"""Add link to source text on VCS (GitHub)"""
source_link = f"https://github.com/python/peps/blob/main/peps/{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."""
try:
iso_time = _LAST_MODIFIED_TIMES[pep_source_path.stem]
except KeyError:
return nodes.paragraph()
commit_link = f"https://github.com/python/peps/commits/main/peps/{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")
ret = subprocess.run(args, stdout=subprocess.PIPE, text=True, encoding="utf-8")
if ret.returncode: # non-zero return code
return {}
all_modified = ret.stdout
# remove "peps/" prefix from file names
all_modified = all_modified.replace("\npeps/", "\n")
# set up the dictionary with the *current* files
peps_dir = Path(__file__, "..", "..", "..", "..", "peps").resolve()
last_modified = {path.stem: "" for path in peps_dir.glob("pep-????.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 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()