Sphinx support: theming (#1933)

See #2, #1385 for context. Superseeds #1568.

This is the Sphinx-theming part, building on PR #1930.

### Stylesheets:
- `style.css` - styles
- `mq.css` - media queries

### Jinja2 Templates:
- `page.html` - overarching template

### Javascript:
- `doctools.js` - fixes footnote brackets

### Theme miscellany
- `theme.conf` - sets pygments styles, theme internals
This commit is contained in:
Adam Turner 2021-06-30 20:19:44 +01:00 committed by GitHub
parent 7d727005fd
commit 0d93abf9bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 585 additions and 123 deletions

View File

@ -36,6 +36,10 @@ jobs:
- name: 🔧 Build PEPs
run: make pages -j$(nproc)
# remove the .doctrees folder when building for deployment as it takes two thirds of disk space
- name: 🔥 Clean up files
run: rm -r build/.doctrees/
- name: 🚀 Deploy to GitHub pages
uses: JamesIves/github-pages-deploy-action@4.1.1
with:

View File

@ -2,7 +2,6 @@
import argparse
from pathlib import Path
import shutil
from sphinx.application import Sphinx
@ -25,11 +24,16 @@ def create_parser():
return parser.parse_args()
def create_index_file(html_root: Path):
def create_index_file(html_root: Path, builder: str) -> None:
"""Copies PEP 0 to the root index.html so that /peps/ works."""
pep_zero_path = html_root / "pep-0000" / "index.html"
if pep_zero_path.is_file():
shutil.copy(pep_zero_path, html_root / "index.html")
pep_zero_file = "pep-0000.html" if builder == "html" else "pep-0000/index.html"
try:
pep_zero_text = html_root.joinpath(pep_zero_file).read_text(encoding="utf-8")
except FileNotFoundError:
return None
if builder == "dirhtml":
pep_zero_text = pep_zero_text.replace('="../', '="') # remove relative directory links
html_root.joinpath("index.html").write_text(pep_zero_text, encoding="utf-8")
if __name__ == "__main__":
@ -67,7 +71,8 @@ if __name__ == "__main__":
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:
create_index_file(build_directory)
create_index_file(build_directory, sphinx_builder)

12
conf.py
View File

@ -1,7 +1,7 @@
"""Configuration for building PEPs using Sphinx."""
import sys
from pathlib import Path
import sys
sys.path.append(str(Path("pep_sphinx_extensions").absolute()))
@ -44,3 +44,13 @@ 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
templates_path = ['pep_sphinx_extensions/pep_theme/templates'] # Theme template relative paths from `confdir`

View File

@ -238,12 +238,9 @@ could be mentioned here.
https://mail.python.org/pipermail/python-dev/2003-August/037795.html
.. [3] Thread on python-dev with subject
.. [Python-Dev] pre-PEP: Resource-Release Support for Generators
starting at
https://mail.python.org/pipermail/python-dev/2003-August/037803.html
`[Python-Dev] pre-PEP: Resource-Release Support for Generators`
starting at
https://mail.python.org/pipermail/python-dev/2003-August/037803.html
Copyright
=========

View File

@ -147,11 +147,11 @@ The "pip3" command will support two new command-line options that are used
in the boostrapping, and otherwise ignored. They control where the pip
implementation is installed:
--bootstrap
``--bootstrap``
Install to the user's packages directory. The name of this option is chosen
to promote it as the preferred installation option.
--bootstrap-to-system
``--bootstrap-to-system``
Install to the system site-packages directory.
These command-line options will also need to be implemented, but otherwise

View File

@ -22,24 +22,6 @@ any daemon regardless of what else the program may need to do.
This PEP introduces a package to the Python standard library that
provides a simple interface to the task of becoming a daemon process.
.. contents::
..
Table of Contents:
Abstract
Specification
Example usage
Interface
``DaemonContext`` objects
Motivation
Rationale
Correct daemon behaviour
A daemon is not a service
Reference Implementation
Other daemon implementations
References
Copyright
============
PEP Deferral
============

View File

@ -5,8 +5,10 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from docutils.writers.html5_polyglot import HTMLTranslator
from sphinx.environment import BuildEnvironment
from sphinx.environment import default_settings
from pep_sphinx_extensions import config
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
@ -26,19 +28,31 @@ default_settings |= {
"_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: {}
def _depart_maths():
pass # No-op callable for the type checker
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"
def setup(app: Sphinx) -> dict[str, bool]:
"""Initialize Sphinx extension."""
# Register plugin logic
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
app.set_translator("html", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (html builder)
app.set_translator("dirhtml", pep_html_translator.PEPTranslator) # Docutils Node Visitor overrides (dirhtml builder)
app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook
app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used
# Mathematics rendering
inline_maths = HTMLTranslator.visit_math, _depart_maths

View File

@ -1,6 +1,6 @@
from sphinx import roles
from pep_sphinx_extensions.config import pep_url
from pep_sphinx_extensions import config
class PEPRole(roles.PEP):
@ -8,9 +8,8 @@ class PEPRole(roles.PEP):
def build_uri(self) -> str:
"""Get PEP URI from role text."""
base_url = self.inliner.document.settings.pep_base_url
pep_num, _, fragment = self.target.partition("#")
pep_base = base_url + pep_url.format(int(pep_num))
pep_str, _, fragment = self.target.partition("#")
pep_base = config.pep_url.format(int(pep_str))
if fragment:
return f"{pep_base}#{fragment}"
return pep_base

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from pathlib import Path
from docutils import nodes
@ -14,21 +16,20 @@ class PEPContents(transforms.Transform):
def apply(self) -> None:
if not Path(self.document["source"]).match("pep-*"):
return # not a PEP file, exit early
# Create the contents placeholder section
title = nodes.title("", "Contents")
contents_topic = nodes.topic("", title, classes=["contents"])
title = nodes.title("", "", nodes.Text("Contents"))
contents_section = nodes.section("", title)
if not self.document.has_name("contents"):
contents_topic["names"].append("contents")
self.document.note_implicit_target(contents_topic)
contents_section["names"].append("contents")
self.document.note_implicit_target(contents_section)
# Add a table of contents builder
pending = nodes.pending(Contents)
contents_topic += pending
contents_section += pending
self.document.note_pending(pending)
# Insert the toc after title and PEP headers
self.document.children[0].insert(2, contents_topic)
self.document.children[0].insert(2, contents_section)
# Add a horizontal rule before contents
transition = nodes.transition()
@ -37,7 +38,7 @@ class PEPContents(transforms.Transform):
class Contents(parts.Contents):
"""Build Table of Contents from document."""
def __init__(self, document, startnode=None):
def __init__(self, document: nodes.document, startnode: nodes.Node | None = None):
super().__init__(document, startnode)
# used in parts.Contents.build_contents
@ -45,19 +46,34 @@ class Contents(parts.Contents):
self.backlinks = None
def apply(self) -> None:
# used in parts.Contents.build_contents
self.toc_id = self.startnode.parent["ids"][0]
self.backlinks = self.document.settings.toc_backlinks
# let the writer (or output software) build the contents list?
if getattr(self.document.settings, "use_latex_toc", False):
# move customisation settings to the parent node
self.startnode.parent.attributes.update(self.startnode.details)
self.startnode.parent.remove(self.startnode)
contents = self.build_contents(self.document[0][4:]) # skip PEP title, headers, <hr/>, and contents
if contents:
self.startnode.replace_self(contents)
else:
contents = self.build_contents(self.document[0])
if contents:
self.startnode.replace_self(contents)
else:
# if no contents, remove the empty placeholder
self.startnode.parent.parent.remove(self.startnode.parent)
# if no contents, remove the empty placeholder
self.startnode.parent.parent.remove(self.startnode.parent)
def build_contents(self, node: nodes.Node | list[nodes.Node], _level: None = None):
entries = []
children = getattr(node, "children", node)
for section in children:
if not isinstance(section, nodes.section):
continue
title = section[0]
# remove all pre-existing hyperlinks in the title (e.g. PEP references)
while (link_node := title.next_node(nodes.reference)) is not None:
link_node.replace_self(link_node[0])
ref_id = section['ids'][0]
title["refid"] = ref_id # Add a link to self
entry_text = self.copy_and_filter(title)
reference = nodes.reference("", "", refid=ref_id, *entry_text)
item = nodes.list_item("", nodes.paragraph("", "", reference))
item += self.build_contents(section) # recurse to add sub-sections
entries.append(item)
if entries:
return nodes.bullet_list('', *entries)
return []

View File

@ -69,43 +69,43 @@ class PEPFooter(transforms.Transform):
# If there are no references after TargetNotes has finished, remove the
# references section
pending = nodes.pending(misc.CallBack, details={"callback": self.cleanup_callback})
pending = nodes.pending(misc.CallBack, details={"callback": _cleanup_callback})
reference_section.append(pending)
self.document.note_pending(pending, priority=1)
# Add link to source text and last modified date
self.add_source_link(pep_source_path)
self.add_commit_history_info(pep_source_path)
if pep_source_path.stem != "pep-0000":
self.document += _add_source_link(pep_source_path)
self.document += _add_commit_history_info(pep_source_path)
@staticmethod
def cleanup_callback(pending: nodes.pending) -> None:
"""Remove an empty "References" section.
Called after the `references.TargetNotes` transform is complete.
def _cleanup_callback(pending: nodes.pending) -> None:
"""Remove an empty "References" section.
"""
if len(pending.parent) == 2: # <title> and <pending>
pending.parent.parent.remove(pending.parent)
Called after the `references.TargetNotes` transform is complete.
def add_source_link(self, pep_source_path: Path) -> None:
"""Add link to source text on VCS (GitHub)"""
source_link = config.pep_vcs_url + pep_source_path.name
link_node = nodes.reference("", source_link, refuri=source_link)
span_node = nodes.inline("", "Source: ", link_node)
self.document.append(span_node)
"""
if len(pending.parent) == 2: # <title> and <pending>
pending.parent.parent.remove(pending.parent)
def add_commit_history_info(self, pep_source_path: Path) -> None:
"""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):
return None
commit_link = config.pep_commits_url + pep_source_path.name
link_node = nodes.reference("", f"{dt.isoformat()}Z", refuri=commit_link)
span_node = nodes.inline("", "Last modified: ", link_node)
self.document.append(nodes.line("", "", classes=["zero-height"]))
self.document.append(span_node)
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
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):
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)
return nodes.paragraph("", "Last modified: ", link_node)

View File

@ -1,12 +1,13 @@
from __future__ import annotations
from pathlib import Path
import re
from docutils import nodes
from docutils import transforms
from docutils.transforms import peps
from sphinx import errors
from pep_sphinx_extensions.config import pep_url
from pep_sphinx_extensions import config
from pep_sphinx_extensions.pep_processor.transforms import pep_zero
@ -69,21 +70,18 @@ class PEPHeaders(transforms.Transform):
raise PEPParsingError(msg)
para = body[0]
if name in {"author", "bdfl-delegate", "pep-delegate", "sponsor"}:
if name in {"author", "bdfl-delegate", "pep-delegate", "discussions-to", "sponsor"}:
# mask emails
for node in para:
if isinstance(node, nodes.reference):
pep_num = pep if name == "discussions-to" else -1
node.replace_self(peps.mask_email(node, pep_num))
pep_num = pep if name == "discussions-to" else None
node.replace_self(_mask_email(node, pep_num))
elif name in {"replaces", "superseded-by", "requires"}:
# replace PEP numbers with normalised list of links to PEPs
new_body = []
space = nodes.Text(" ")
for ref_pep in re.split(r",?\s+", body.astext()):
new_body.append(nodes.reference(
ref_pep, ref_pep,
refuri=(self.document.settings.pep_base_url + pep_url.format(int(ref_pep)))))
new_body.append(space)
new_body += [nodes.reference("", ref_pep, refuri=config.pep_url.format(int(ref_pep)))]
new_body += [nodes.Text(", ")]
para[:] = new_body[:-1] # drop trailing space
elif name in {"last-modified", "content-type", "version"}:
# Mark unneeded fields
@ -94,7 +92,7 @@ class PEPHeaders(transforms.Transform):
field.parent.remove(field)
def _mask_email(ref: nodes.reference, pep_num: int = -1) -> nodes.reference:
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.
@ -105,15 +103,12 @@ def _mask_email(ref: nodes.reference, pep_num: int = -1) -> nodes.reference:
If given a PEP number `pep_num`, add a default email subject.
"""
if "refuri" in ref and ref["refuri"].startswith("mailto:"):
non_masked_addresses = {"peps@python.org", "python-list@python.org", "python-dev@python.org"}
if ref['refuri'].removeprefix("mailto:").strip() in non_masked_addresses:
replacement = ref[0]
else:
replacement_text = ref.astext().replace("@", "&#32;&#97;t&#32;")
replacement = nodes.raw('', replacement_text, format="html")
if pep_num != -1:
replacement['refuri'] += f"?subject=PEP%20{pep_num}"
return replacement
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,7 +1,10 @@
from pathlib import Path
from docutils import nodes
import docutils.transforms as transforms
from docutils import transforms
from docutils import utils
from docutils.parsers.rst import roles
from docutils.parsers.rst import states
class PEPTitle(transforms.Transform):
@ -34,16 +37,20 @@ class PEPTitle(transforms.Transform):
pep_title_string = f"PEP {pep_number} -- {pep_title}" # double hyphen for en dash
# Generate the title section node and its properties
pep_title_node = nodes.section()
text_node = nodes.Text(pep_title_string, pep_title_string)
title_node = nodes.title(pep_title_string, "", text_node)
title_node["classes"].append("page-title")
name = " ".join(title_node.astext().lower().split()) # normalise name
pep_title_node["names"].append(name)
pep_title_node += title_node
title_nodes = _line_to_nodes(pep_title_string)
pep_title_node = nodes.section("", nodes.title("", "", *title_nodes, classes=["page-title"]), names=["pep-content"])
# Insert the title node as the root element, move children down
document_children = self.document.children
self.document.children = [pep_title_node]
pep_title_node.extend(document_children)
self.document.note_implicit_target(pep_title_node, pep_title_node)
def _line_to_nodes(text: str) -> list[nodes.Node]:
"""Parse RST string to nodes."""
document = utils.new_document("<inline-rst>")
document.settings.pep_references = document.settings.rfc_references = False # patch settings
states.RSTStateMachine(state_classes=states.state_classes, initial_state="Body").run([text], document) # do parsing
roles._roles.pop("", None) # restore the "default" default role after parsing a document
return document[0].children

View File

@ -2,7 +2,7 @@ from docutils import nodes
from docutils import transforms
from docutils.transforms import peps
from pep_sphinx_extensions.config import pep_url
from pep_sphinx_extensions import config
class PEPZero(transforms.Transform):
@ -68,7 +68,7 @@ class PEPZeroSpecial(nodes.SparseNodeVisitor):
if isinstance(para, nodes.paragraph) and len(para) == 1:
pep_str = para.astext()
try:
ref = self.document.settings.pep_base_url + pep_url.format(int(pep_str))
ref = config.pep_url.format(int(pep_str))
para[0] = nodes.reference(pep_str, pep_str, refuri=ref)
except ValueError:
pass

View File

@ -0,0 +1,5 @@
/* 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

@ -0,0 +1,95 @@
@charset "UTF-8";
/* Media Queries */
@media (max-width: 32.5em) {
/* Reduce padding & margins for the smallest screens */
section#pep-page-section > header > h1 {
padding-right: 0;
border-right: none;
}
ul.breadcrumbs {
padding: 0 0 .5rem;
}
nav#pep-sidebar {
display: none;
}
table th,
table td {
padding: 0 0.1rem;
}
}
@media (min-width: 32.5em) {
section#pep-page-section {
max-width: 40em;
width: 100%;
margin: 0 auto;
padding: .5rem 1rem 0;
}
}
@media (min-width: 54em) {
section#pep-page-section {
max-width: 75em;
}
section#pep-page-section > article {
max-width: 40em;
width: 74%;
float: right;
margin-right: 0;
font-size: 1rem;
}
nav#pep-sidebar {
width: 24%;
float: left;
margin-right: 2%;
}
}
@media (min-width: 60em) {
section#pep-page-section > article {
max-width: none;
padding-left: 3.2%;
padding-right: 3.2%;
}
}
@media print {
*,
*:before,
*:after {
color: #000 !important;
}
body {
font-size: 10pt;
line-height: 1.67;
}
*[role="main"] a[href]:after {
content: " (" attr(href) ")";
font-size: .75rem;
}
pre,
blockquote {
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
@page {
margin: 0.5cm;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h1,
h2,
h3 {
page-break-after: avoid;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

View File

@ -0,0 +1,292 @@
@charset "UTF-8";
/* Styles for PEPs
Colours:
white:
background
footnotes/references vertical border
#333
body text
#888
blockquote left line
header breadcrumbs separator
link underline (hovered/focussed)
#ccc:
scrollbar
#ddd
header bottom border
horizontal rule
table vertical border
#eee:
link underline
table rows & top/bottom border
PEP header rows
footnotes/references rows
admonition note background
#f8f8f8:
inline code background
#0072aa:
links
# fee:
admonition warning background
*/
/* Set master rules */
* {box-sizing: border-box}
html {
overflow-y: scroll;
-webkit-font-smoothing: antialiased;
margin: 0;
line-height: 1.4rem;
font-weight: normal;
font-size: 1rem;
font-family: "Source Sans Pro", Arial, sans-serif;
}
body {
margin: 0;
color: #333;
background-color: white;
}
section#pep-page-section {
padding: 0.25rem 0.25rem 0;
display: table;
}
/* Reduce margin sizes for body text */
p {margin: .5rem 0}
/* Header rules */
h1.page-title {
line-height: 2.3rem;
font-size: 2rem;
font-weight: bold;
margin-top: 2rem;
margin-bottom: 1.5rem;
}
h2 {
font-size: 1.6rem;
font-weight: bold;
margin-top: 1rem;
margin-bottom: .5rem;
}
h3 {
font-size: 1.4rem;
font-weight: normal;
margin-top: 1rem;
margin-bottom: 0.5rem;
}
h4 {
font-size: 1.2rem;
font-weight: normal;
margin-top: .5rem;
margin-bottom: 0;
}
h5,
h6 {
font-size: 1rem;
font-weight: bold;
margin-top: 0;
margin-bottom: 0;
}
/* Anchor link rules */
a,
a:active,
a:visited {
color: #0072aa;
text-decoration-color: #eee;
display: inline;
}
a:hover,
a:focus {
text-decoration-color: #888;
}
/* Blockquote rules */
blockquote {
font-style: italic;
border-left: 1px solid #888;
margin: .5rem;
padding: .5rem 1rem;
}
blockquote em {
font-style: normal;
}
cite {
font-style: italic;
}
/* Code rules (code literals and Pygments highlighting blocks) */
pre,
code {
font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "DejaVu Sans Mono", Consolas, monospace;
white-space: pre-wrap;
word-wrap: break-word;
-webkit-hyphens: none;
hyphens: none;
}
code.literal {
font-size: .8em;
background-color: #f8f8f8;
}
pre {
padding: .5rem .75rem;
}
/* Definition list rules */
dl dt {
font-weight: bold;
}
dl dd {
margin: 0;
}
/* Horizontal rule rule */
hr {
border: 0;
border-top: 1px solid #ddd;
margin: 1.75rem 0;
}
/*Image rules */
img {
max-width: 100%;
}
a img {
display: block;
margin: 0 auto;
}
/* List rules */
ul,
ol {
padding: 0;
margin: 0 0 0 1.5rem;
}
ul {list-style: square}
ol.arabic {list-style: decimal}
ol.loweralpha {list-style: lower-alpha}
ol.upperalpha {list-style: upper-alpha}
ol.lowerroman {list-style: lower-roman}
ol.upperroman {list-style: upper-roman}
/* Maths rules */
sub,
sup {
font-size: .75em;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {top: -0.5em}
sub {bottom: -0.25em}
/* Table rules */
table {
width: 100%;
border-collapse: collapse;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
table caption {
margin: 1rem 0 .75rem;
}
table tbody tr:nth-of-type(odd) {
background-color: #eee;
}
table th,
table td {
text-align: left;
padding: 0.25rem 0.5rem 0.2rem;
}
table td + td {
border-left: 1px solid #ddd;
}
/* Breadcrumbs rules */
section#pep-page-section > header {
border-bottom: 1px solid #ddd;
}
section#pep-page-section > header > h1 {
font-size: 1.1rem;
margin: 0;
display: inline-block;
padding-right: .6rem;
border-right: 1px solid #888;
}
ul.breadcrumbs {
margin: 0;
padding: .5rem 0 .5rem .4rem;
list-style: none;
display: inline-block;
}
ul.breadcrumbs li {
display: inline;
}
ul.breadcrumbs a {
text-decoration: none;
}
/* Admonitions rules */
div.note,
div.warning {
padding: 0.5rem 0.75rem;
margin-top: 1rem;
margin-bottom: 1rem;
}
div.note {
background-color: #eee;
}
div.warning {
background-color: #fee;
}
p.admonition-title {
font-weight: bold;
}
/* PEP Header / references rules */
dl.rfc2822,
dl.footnote {
display: grid;
grid-template-columns: fit-content(30%) auto;
line-height: 1.875;
width: 100%;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
dl.rfc2822 > dt, dl.rfc2822 > dd,
dl.footnote > dt, dl.footnote > dd {
padding: .25rem .5rem .2rem;
}
dl.rfc2822 > dt:nth-of-type(even), dl.rfc2822 > dd:nth-of-type(even),
dl.footnote > dt:nth-of-type(even), dl.footnote > dd:nth-of-type(even) {
background-color: #eee;
}
dl.footnote > dt {
font-weight: normal;
border-right: 1px solid white;
}
/* Sidebar formatting */
nav#pep-sidebar {
overflow-y: scroll;
position: sticky;
top: 0;
height: 100vh;
scrollbar-width: thin; /* CSS Standards, not *yet* widely supported */
scrollbar-color: #ccc transparent;
}
nav#pep-sidebar::-webkit-scrollbar {width: 6px}
nav#pep-sidebar::-webkit-scrollbar-track {background: transparent}
nav#pep-sidebar::-webkit-scrollbar-thumb {background: #ccc}
nav#pep-sidebar > h2 {
font-size: 1.4rem;
}
nav#pep-sidebar ul {
margin-left: 1rem;
}
nav#pep-sidebar ul a {
text-decoration: none;
}

View File

@ -0,0 +1,37 @@
{# Master template for simple pages (e.g. RST files) #}
<!DOCTYPE html>
<html lang="en-GB">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{ title + " | "|safe + docstitle }}</title>
<link rel="shortcut icon" href="{{ pathto('_static/py.png', resource=True) }}"/>
<link rel="stylesheet" href="{{ pathto('_static/style.css', resource=True) }}" type="text/css" />
<link rel="stylesheet" href="{{ pathto('_static/mq.css', resource=True) }}" type="text/css" />
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', resource=True) }}" type="text/css" />
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<meta name="description" content="Python Enhancement Proposals (PEPs)"/>
</head>
<body>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="{{ pathto("pep-0000") }}">PEP Index</a> &raquo; </li>
<li>{{ title }}</li>
</ul>
</header>
<article>
{{ body }}
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
{{ toc }}
<br />
<strong><a href="https://github.com/python/peps/blob/master/{{sourcename}}">Page Source (GitHub)</a></strong>
</nav>
</section>
<script src="{{ pathto('_static/doctools.js', resource=True) }}"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
[theme]
# Theme options
inherit = none
pygments_style = tango