pydata-sphinx-theme icon indicating copy to clipboard operation
pydata-sphinx-theme copied to clipboard

Show methods and properties of autoclass in right column.

Open SebastianJL opened this issue 4 years ago • 6 comments

I am using pydata-sphinx-theme for the documentation of zfit. (The new version with the pydata theme can be found in the version doc_redesign).

Now it would be very useful to automatically list the methods and properties of a class in the right column without having to write a manual header for each section. I.e.

.. autoclass:: zfit.constraint.GaussianConstraint
    :members:

Instead of

.. py:currentmodule:: zfit.constraint

.. autoclass:: GaussianConstraint

create_covariance
------------------------
.. automethod:: GaussianConstraint.create_covariance

covariance
------------------------
.. automethod:: GaussianConstraint.covariance

etc.

So my question now is:

Is this already possible? And if not would we have to change something about pydata theme or does this change have to be made in autodoc, i.e. generating headers for methods.

I am happy to help with implementation if you give me some guidance.

SebastianJL avatar Jun 25 '20 09:06 SebastianJL

That sounds like an interesting idea, we just hit a similar issue and this would solve it. Are there any plans already for this?

jonas-eschle avatar Jul 01 '20 16:07 jonas-eschle

Hi, me too use this great theme to generate documentation for python modules. Like @SebastianJL wrote, it would be really awesome to use just .. autoclass:: myclass or maybe even .. automodule:: mymod. Is it already possible?

The README says it uses toctree's first, second and third level to populate top, left and right bar of this theme, bus Sphinx' autodoc extention documentation says no word about if a module/class/method/etc. ends up in the toctree or not.

I am a bloody beginner in this area so any help is appreciated :)

samweisgamdschie avatar Nov 24 '20 19:11 samweisgamdschie

I've been having a bit of a dabble with this

I created a new template like _template/class_page_toc.html

{% set class_toc = get_class_toc() %}

<nav id="bd-toc-nav">
    <ul class="visible nav section-nav flex-column">
    <li class="toc-h1 nav-item toc-entry" style="font-weight: 400; margin-left: 1rem; margin-bottom: 5px;">
        {{class_toc.pretitle}}{{ class_toc.title }}
    </li>
    {% for item in class_toc.menu_items %}
        <li class="toc-h2 nav-item toc-entry">
            <a class="reference internal nav-link" href="{{ item.link }}">{{ item.title }}</a>
        </li>
    {% endfor %}
    </ul>
</nav>

And then in your pydata theme you need to add a snippet like

    def get_class_toc():
        soup = bs(context["body"], "html.parser")

        matches = soup.find_all('dl')
        if matches is None or len(matches) is 0:
            return ""

        out = {
            'title': '',
            'menu_items': []
        }

        #  remove the class dt
        pyclass = matches.pop(0)
        pyclass = pyclass.find('dt')
        if pyclass is not None:
            out['title'] = pyclass.get('id')

        for match in matches:
            match_dt = match.find('dt')
            link = match.find(class_="headerlink")
            if link is not None:
                out['menu_items'].append({
                    'title': match_dt.get('id'),
                    'link': link['href']
                })

        return out

This gives me something like

Datacube Class — Open Data Cube 1 8-FIXME documentation 2021-06-26 17-37-51

Hope that helps somebody

rowanwins avatar Jun 26 '21 07:06 rowanwins

Hello, Thanks @rowanwins ! To give more precision you can add the following code in your conf.py :

def _setup_navbar_side_toctree(app: Any):

    def add_class_toctree_function(app: Any, pagename: Any, templatename: Any, context: Any, doctree: Any):
        def get_class_toc() -> Any:
            soup = BeautifulSoup(context["body"], "html.parser")

            matches = soup.find_all('dl')
            if matches is None or len(matches) == 0:
                return ""
            items = []
            deeper_depth = matches[0].find('dt').get('id').count(".")
            for match in matches:
                match_dt = match.find('dt')
                if match_dt is not None and match_dt.get('id') is not None:
                    current_title = match_dt.get('id')
                    current_depth = match_dt.get('id').count(".")
                    current_link = match.find(class_="headerlink")
                    if current_link is not None:
                        if deeper_depth > current_depth:
                            deeper_depth = current_depth
                        if deeper_depth == current_depth:
                            items.append({
                                "title": current_title,
                                "link": current_link["href"],
                                "attributes_and_methods": []
                            })
                        if deeper_depth < current_depth:
                            items[-1]["attributes_and_methods"].append(
                                {
                                    "title": current_title,
                                    "link": current_link["href"],
                                }
                            )
            return items
        context["get_class_toc"] = get_class_toc

    app.connect("html-page-context", add_class_toctree_function)



def setup(app: Any):
    for setup_function in [
        _setup_navbar_side_toctree,
    ]:
        setup_function(app)

and add add the folowing entry in conf.py's html_theme_options:

"page_sidebar_items": ["page-toc", "class-page-toc"],

and add the following html in template:

{% set class_toc = get_class_toc() %}

<nav id="bd-toc-nav", style="padding-top: 1rem;">
    <ul class="visible nav section-nav flex-column">
        {% for item in class_toc %}
        <li class="toc-h1 nav-item toc-entry">
            <a class="reference internal nav-link" href="{{ item.link }}">{{ item.title }}</a>
        </li>
        <ul class="visible nav section-nav flex-column">
            {% for attr in item.attributes_and_methods %}
            <li class="toc-h2 nav-item toc-entry">
                <a class="reference internal nav-link" href="{{ attr.link }}">{{ attr.title }}</a>
            </li>
            {% endfor %}
        </ul>
        {% endfor %}
    </ul>
</nav>

bloussou avatar May 03 '22 07:05 bloussou

@choldgraf, do you know if we have some snippets/recipes/wiki section to collect these examples from the community? I think this is probably beyond the configuration section in the docs... although it might be a new section, I guess... Thoughts?

damianavila avatar May 13 '22 20:05 damianavila

@damianavila, I think github issues are well rferenced on Google so someone with the same need would ends up here eventually. I'm not sure we should duplicate them in the doc. +1 on setting it to wontfix and closing the issue as it's a heavy customisation of sphinx behaviours and @bloussou solutions seems to work

12rambau avatar Sep 29 '22 15:09 12rambau

It seems Furo is integrating it in its secondary sidebar, we should do it as well: https://pradyunsg.me/furo/kitchen-sink/api/#furo._demo_module.show_warning

12rambau avatar Oct 19 '22 05:10 12rambau

I think that this works on our site now, no?

I see this rST on the API page here.

.. automodule:: urllib.parse
    :members:

and here's what it looks like:

image

I'm going to close this assuming it now works, but please re-open if it doesn't work on main.

choldgraf avatar Jan 11 '23 19:01 choldgraf

Reopen

I'd like to reopen this issue because:

  • @choldgraf By default, only top-level functions and classes are shown, we'd like to control the visibility of the nested ones.
  • Previous methods don't work anymore since current version used secondary_sidebar_items and there is no page_sidebar_items option (see layout).

Updated Solution

To show class methods and attributes/inner function, we need to set show_toc_level as noted here, i.e., just specify in conf.py:

html_theme_options = {
  "show_toc_level": 2 # can increase, e.g., if there are nested classes
}

Which gives the following look:

Screenshot 2024-01-17 155720

Feature Request

There are still 2 problems I face and I wonder if these could be addressed:

  1. If the name of some item is too long, it is cropped - it would be better if the name could wrap (though this could be avoided by increasing the secondary sidebar width).
  2. There is an unnecessary pre-fix (BaseGlassesModel in the given example) for each autodoc-generated method/attribute name in the sidebar. Perhaps we could control whether or not to show the prefixes?

Workaround

The current workaround (for level 2 page-toc) is to add the following code to conf.py:

from pathlib import Path
from bs4 import BeautifulSoup


def process_in_page_toc(app, exception):
    if app.builder.format != "html":
        return

    for pagename in app.env.found_docs:
        if not isinstance(pagename, str):
            continue

        with (Path(app.outdir) / f"{pagename}.html").open("r") as f:
            # Parse HTML using BeautifulSoup html parser
            soup = BeautifulSoup(f.read(), "html.parser")

            for li in soup.find_all("li", class_="toc-h3 nav-item toc-entry"):
                if span := li.find("span"):
                    # Modify the toc-nav span element here
                    span.string = span.string.split(".")[-1]

        with (Path(app.outdir) / f"{pagename}.html").open("w") as f:
            # Write back HTML
            f.write(str(soup))


def setup(app):
    app.connect("build-finished", process_in_page_toc)

Which gives the following look: Screenshot 2024-01-17 155821

mantasu avatar Jan 17 '24 16:01 mantasu

This is related with the anchors created by autosummary and the reason why you are not seeing them is because the scrollspy function from Bootstrap is not catching anchors with ".".

We're trying to find a solution and we are discussing the problem in https://github.com/pydata/pydata-sphinx-theme/issues/1435

12rambau avatar Jan 18 '24 12:01 12rambau