Skip to content
Open
7 changes: 4 additions & 3 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'implementation_detail',
'issue_role',
'lexers',
'meta_navigation',
'misc_news',
'profiling_trace',
'pydoc_topics',
Expand Down Expand Up @@ -315,9 +316,9 @@

# Custom sidebar templates, filenames relative to this file.
html_sidebars = {
# Defaults taken from https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_sidebars
# Removes the quick search block
'**': ['localtoc.html', 'relations.html', 'customsourcelink.html'],
# Sidebar configuration: https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_sidebars
# Custom sidebars without quick search; the homepage uses a dedicated sidebar.
'**': ['localtoc.html', 'relations.html', 'pageactions.html'],
'index': ['indexsidebar.html'],
}

Expand Down
154 changes: 154 additions & 0 deletions Doc/tools/extensions/meta_navigation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
"""Include generated HTML pages in the previous/next navigation flow."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from sphinx.locale import _ as sphinx_gettext

if TYPE_CHECKING:
from docutils.nodes import document
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata


_TEMPLATE_SOURCES = {
'download': 'tools/templates/download.html',
'search': 'tools/templates/search.html',
}


def _has_module_index(app: Sphinx) -> bool:
return any(
name == 'py-modindex' for name, *_ in app.builder.domain_indices
)


def _genindex_pages(app: Sphinx, context: dict[str, Any]) -> list[str]:
if not app.builder.use_index:
return []

pages = ['genindex']
if app.config.html_split_index:
pages.extend(
f'genindex-{key}'
for key, _entries in context.get('genindexentries', ())
)
pages.append('genindex-all')
return pages


def _page_title(app: Sphinx, pagename: str) -> str:
if title_node := app.env.titles.get(pagename):
return app.builder.render_partial(title_node)['title']

if pagename == 'py-modindex':
return sphinx_gettext('Python Module Index')
if pagename in {'genindex', 'genindex-all'}:
return sphinx_gettext('Index')
if pagename.startswith('genindex-'):
return (
f"{sphinx_gettext('Index')} – {pagename.removeprefix('genindex-')}"
)
if pagename == 'search':
return sphinx_gettext('Search')
if pagename == 'download':
return sphinx_gettext('Download')
raise ValueError(f'unknown generated page: {pagename}')


def _splice_flow(
app: Sphinx,
pagename: str,
context: dict[str, Any],
flow: list[str],
) -> None:
try:
position = flow.index(pagename)
except ValueError:
return

relation_links = {
rellink[2]: rellink
for rellink in context['rellinks']
if rellink[2] in {'N', 'P'}
}
for offset, direction, accesskey in (
(1, 'next', 'N'),
(-1, 'prev', 'P'),
):
target_position = position + offset
if not 0 <= target_position < len(flow):
context.pop(direction, None)
relation_links.pop(accesskey, None)
continue

target = flow[target_position]
title = _page_title(app, target)
context[direction] = {
'link': context['pathto'](target),
'title': title,
}
relation_links[accesskey] = (
target,
title,
accesskey,
sphinx_gettext('previous' if direction == 'prev' else 'next'),
)

context['rellinks'] = [
rellink
for rellink in context['rellinks']
if rellink[2] not in {'N', 'P'}
]
context['rellinks'].extend(
relation_links[accesskey]
for accesskey in ('N', 'P')
if accesskey in relation_links
)


def add_meta_page_relations(
app: Sphinx,
pagename: str,
_templatename: str,
context: dict[str, Any],
_doctree: document | None,
) -> None:
if app.builder.name != 'html':
return

if pagename in _TEMPLATE_SOURCES:
context['page_source_path'] = _TEMPLATE_SOURCES[pagename]

flow = []
docname = app.config.root_doc
while docname is not None:
if docname == 'glossary':
# prepend modindex and genindex to glossary
if _has_module_index(app):
flow.append('py-modindex')
flow.extend(_genindex_pages(app, context))

flow.append(docname)
if docname == 'glossary' and app.builder.search:
flow.append('search')
if (
docname == 'bugs'
and 'download' in app.config.html_additional_pages
):
flow.append('download')

docname = app.builder.relations[docname][2]

_splice_flow(app, pagename, context, flow)


def setup(app: Sphinx) -> ExtensionMetadata:
app.connect('html-page-context', add_meta_page_relations)

return {
'version': '1.0',
'parallel_read_safe': True,
'parallel_write_safe': True,
}
34 changes: 0 additions & 34 deletions Doc/tools/templates/customsourcelink.html

This file was deleted.

4 changes: 4 additions & 0 deletions Doc/tools/templates/indexcontent.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ <h1>{{ docstitle|e }}</h1>
<p>
{% trans %}Welcome! This is the official documentation for Python {{ release }}.{% endtrans %}
</p>
{#
Remember to update root contents.rst when reordering chapters, and cover new static pages from main sections
in meta_navigation extension!
#}
<p><strong>{% trans %}Documentation sections:{% endtrans %}</strong></p>
<div class="contentstable">
<ul>
Expand Down
44 changes: 44 additions & 0 deletions Doc/tools/templates/pageactions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{%- if page_source_path is defined %}
{%- set source_path = page_source_path %}
{%- elif page_source_suffix is defined %}
{%- set source_path = pagename + page_source_suffix %}
{%- else %}
{%- set source_path = none %}
{%- endif %}
{%- set is_source_page = page_source_suffix is defined %}
<div role="note" aria-label="page actions">
<h3>{{ _('This page') }}</h3>
<ul class="this-page-menu">
<li><a href="{{ pathto('bugs') }}">{% trans %}Report a bug{% endtrans %}</a></li>
<li><a class="improvepage" href="{{ pathto('improve-page-nojs') }}">{% trans %}Improve this page{% endtrans %}</a></li>
{% if source_path %}
<li>
<a href="https://github.com/python/cpython/blob/{{ source_branch }}/Doc/{{ source_path }}?plain=1"
rel="nofollow">{{ _('Show source') }}
</a>
</li>
{% endif %}
{% if language != "en" and source_branch != "main" and is_source_page %}
<li>
<a href="https://github.com/python/python-docs-{{ language | replace('_', '-') | lower }}/blob/{{ source_branch }}/{{ pagename }}.po?plain=1"
rel="nofollow">{{ _('Show translation source') }}</a>
</li>
{% endif %}
</ul>
</div>
{% if source_path %}
<script>
document.addEventListener('DOMContentLoaded', () => {
const title = document.querySelector('.body h1').textContent;
const elements = document.querySelectorAll('.improvepage');
const pageurl = window.location.href.split('?')[0];
elements.forEach(element => {
const url = new URL(element.href.split('?')[0].replace("-nojs", ""));
url.searchParams.set('pagetitle', title);
url.searchParams.set('pageurl', pageurl);
url.searchParams.set('pagesource', "{{ source_path }}");
element.href = url.toString();
});
});
</script>
{% endif %}
Loading