Skip to content

Commit 60b88cb

Browse files
committed
[no ci] docs: populate second toc with class methods
1 parent 6bc8b67 commit 60b88cb

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

docsrc/source/conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
current = "local"
2727

2828
sys.path.insert(0, os.path.abspath("../.."))
29+
sys.path.insert(0, os.path.abspath("../sphinxext"))
2930

3031
# might set copyright end to wrong year -> remove
3132
if "SOURCE_DATE_EPOCH" in os.environ:
@@ -55,6 +56,7 @@
5556
"sphinx.ext.intersphinx",
5657
"sphinx_design",
5758
"sphinxcontrib.bibtex",
59+
"override_pst_pagetoc",
5860
]
5961

6062
intersphinx_mapping = {
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Adapted from https://github.com/scikit-learn/scikit-learn
2+
# doc/sphinxext/override_pst_pagetoc.py, which is under the following license:
3+
#
4+
# BSD 3-Clause License
5+
#
6+
# Copyright (c) 2007-2024 The scikit-learn developers.
7+
# All rights reserved.
8+
#
9+
# Redistribution and use in source and binary forms, with or without
10+
# modification, are permitted provided that the following conditions are met:
11+
#
12+
# * Redistributions of source code must retain the above copyright notice, this
13+
# list of conditions and the following disclaimer.
14+
#
15+
# * Redistributions in binary form must reproduce the above copyright notice,
16+
# this list of conditions and the following disclaimer in the documentation
17+
# and/or other materials provided with the distribution.
18+
#
19+
# * Neither the name of the copyright holder nor the names of its
20+
# contributors may be used to endorse or promote products derived from
21+
# this software without specific prior written permission.
22+
#
23+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33+
34+
from functools import cache
35+
36+
from sphinx.util.logging import getLogger
37+
38+
logger = getLogger(__name__)
39+
40+
41+
def override_pst_pagetoc(app, pagename, templatename, context, doctree):
42+
"""Overrides the `generate_toc_html` function of pydata-sphinx-theme for API."""
43+
44+
@cache
45+
def generate_api_toc_html(kind="html"):
46+
"""Generate the in-page toc for an API page.
47+
48+
This relies on the `generate_toc_html` function added by pydata-sphinx-theme
49+
into the context. We save the original function into `pst_generate_toc_html`
50+
and override `generate_toc_html` with this function for generated API pages.
51+
52+
The pagetoc of an API page would look like the following:
53+
54+
<ul class="visible ..."> <-- Unwrap
55+
<li class="toc-h1 ..."> <-- Unwrap
56+
<a class="..." href="#">{{obj}}</a> <-- Decompose
57+
58+
<ul class="visible ...">
59+
<li class="toc-h2 ...">
60+
...object
61+
<ul class="..."> <-- Set visible if exists
62+
<li class="toc-h3 ...">...method 1</li> <-- Shorten
63+
<li class="toc-h3 ...">...method 2</li> <-- Shorten
64+
...more methods <-- Shorten
65+
</ul>
66+
</li>
67+
<li class="toc-h2 ...">...gallery examples</li>
68+
</ul>
69+
70+
</li> <-- Unwrapped
71+
</ul> <-- Unwrapped
72+
"""
73+
soup = context["pst_generate_toc_html"](kind="soup")
74+
75+
try:
76+
# Unwrap the outermost level
77+
soup.ul.unwrap()
78+
soup.li.unwrap()
79+
soup.a.decompose()
80+
81+
# Get all toc-h2 level entries, where the first one should be the function
82+
# or class, and the second one, if exists, should be the examples; there
83+
# should be no more than two entries at this level for generated API pages
84+
lis = soup.ul.select("li.toc-h2")
85+
main_li = lis[0]
86+
meth_list = main_li.ul
87+
88+
if meth_list is not None:
89+
# This is a class API page, we remove the class name from the method
90+
# names to make them better fit into the secondary sidebar; also we
91+
# make the toc-h3 level entries always visible to more easily navigate
92+
# through the methods
93+
meth_list["class"].append("visible")
94+
for meth in meth_list.find_all("li", {"class": "toc-h3"}):
95+
target = meth.a.code.span
96+
target.string = target.string.split(".", 1)[1]
97+
98+
# This corresponds to the behavior of `generate_toc_html`
99+
return str(soup) if kind == "html" else soup
100+
101+
except Exception as e:
102+
# Upon any failure we return the original pagetoc
103+
logger.warning(f"Failed to generate API pagetoc for {pagename}: {e}; falling back")
104+
return context["pst_generate_toc_html"](kind=kind)
105+
106+
# Override the pydata-sphinx-theme implementation for generate API pages
107+
if pagename.startswith("api/"):
108+
context["pst_generate_toc_html"] = context["generate_toc_html"]
109+
context["generate_toc_html"] = generate_api_toc_html
110+
111+
112+
def setup(app):
113+
# Need to be triggered after `pydata_sphinx_theme.toctree.add_toctree_functions`,
114+
# and since default priority is 500 we set 900 for safety
115+
app.connect("html-page-context", override_pst_pagetoc, priority=900)

0 commit comments

Comments
 (0)