Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show methods and properties of autoclass in right column. #215

Closed
SebastianJL opened this issue Jun 25, 2020 · 10 comments
Closed

Show methods and properties of autoclass in right column. #215

SebastianJL opened this issue Jun 25, 2020 · 10 comments
Milestone

Comments

@SebastianJL
Copy link

SebastianJL commented Jun 25, 2020

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 SebastianJL changed the title Show methods and properties of class in right column. Show methods and properties of autoclass in right column. Jun 25, 2020
@jonas-eschle
Copy link

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

@samweisgamdschie
Copy link

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 :)

@rowanwins
Copy link

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

@bloussou
Copy link

bloussou commented May 3, 2022

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>

@damianavila
Copy link
Collaborator

@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?

@12rambau
Copy link
Collaborator

@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
Copy link
Collaborator

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 12rambau added this to the 0.12 milestone Oct 19, 2022
@12rambau 12rambau modified the milestones: 0.12, 0.13 Nov 17, 2022
@choldgraf
Copy link
Collaborator

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.

@mantasu
Copy link

mantasu commented Jan 17, 2024

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

@12rambau
Copy link
Collaborator

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 #1435

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants