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

feature: Hide imported symbols (objects, modules etc) #656

Open
andreisorokin-astrazeneca opened this issue Mar 12, 2024 · 5 comments
Open
Assignees
Labels
feature New feature or request

Comments

@andreisorokin-astrazeneca
Copy link

Is your feature request related to a problem? Please describe.

TOC for module shows imported symbols in addition to the symbols that are defined in the module.

That clutter the TOC because they are not distinguished from symbols defined in the module.

Moreover, if I create TOC of my package, there will be a lot of duplications - the same symbols will be listed where they were defined and were they imported.

Describe the solution you'd like

Option to hide the imported symbols.

Describe alternatives you've considered

I do not see any other solution.

Additional context

::: mypackage
    options:
      heading_level: 2
      show_submodules: true

We will see something like that (full paths not in the TOC but inside the page, just illustration how confusing it is)

mod mypackage
  class mypackage.Class1 # imported
mod module1
  class mypackage.module1.Class1 # defined
mod module2
  class mypackage.module2.Class1  # imported
@pawamoy
Copy link
Member

pawamoy commented Mar 12, 2024

Hello, thanks for the feature request 🙂

To know which objects to render, mkdocstrings-python uses Griffe's is_public() method on each object (example: Attribute.is_public).

Looking at the code, these imported classes should not be considered public 🤔

Is it possible that these imported classes are listed in your modules' __all__ attributes?

@andreisorokin-astrazeneca
Copy link
Author

You are absolutely right - they are in the package's __all__
But now they are listed in each module of the package :(

@pawamoy
Copy link
Member

pawamoy commented Mar 12, 2024

Well, adding such imported classes to __all__ tells Griffe (or, by convention, most of the Python tools dealing with "public APIs") that these classes are public (or "exposed", "exported"). Then, in downstream tools like mkdocstrings and its Python handler, if you let them choose which object to render, they'll do their best to understand the dev's intent and will pick up objects that are considered public, that are meant to be consumed by users. If you don't want that, you have two options:

  • Don't mark these objects as public if they're not really public (remove them from __all__). In your case, I'd recommend removing Class1 from module2's __all__.
  • Explicitly tell docs rendering tools which objects you want to render. In mkdocstrings-python, it could be done with show_submodules: false (if you expose all public objects in the top-level module for example). Or you could render each public member one by one in the right Markdown pages, etc.. In short: manually telling which objects to render rather than letting the tool render everything at once, recursively.

We actually have the same "issue" in Griffe itself: most public objects are rendered in the top-level page (https://mkdocstrings.github.io/griffe/reference/griffe/#griffe.Attribute) and again in submodule pages (https://mkdocstrings.github.io/griffe/reference/griffe/dataclasses/#griffe.dataclasses.Attribute). But that's entirely my fault: my public API is ambiguous. Some objects are explicitly exposed in multiple places (in griffe and in griffe.dataclasses for example). To make it unambiguous, I should "hide" some of them so that only one public location remains. Related and interesting dicsussion: mkdocstrings/griffe#230. My plan is to actually make all submodules private (or even move them into a private package), and expose everything in the top-level griffe module. Then, instead of rendering everything in one page (it would be an enormous page, not the best UX), I will carefully craft pages and the objects they render. That demands more work, but it's usually the only way when you want to render API docs in the best way.

Now, I still have ideas to improve the situation when API docs rendering is automated (for example with show_submodules: true or with scripts that render everything, such as in our automatic code reference pages recipe).

One idea is that we could have options to render links to already rendered objects instead of rendering them again. Either explicitly, by adding a "preferred location" marker on objects, or implicitly, for example by considering the public location to be the highest one in the package (griffe.Attribute is higher than griffe.dataclasses.Attribute so it would be considered the preferred location).

There are probably more ways to achieve such improvements, but the thing is Python doesn't have a standard for marking objects as public: we only have somewhat weak conventions, like prefixing names with _ to mark private objects, and/or listing names in __all__ (which is originally only intended for wildcard imports). Without standards, it's hard to implement something that works for everyone.

@andreisorokin-astrazeneca
Copy link
Author

thank you for such detailed explanation.
Now I see how complicated is the task.

The mkdocstrings is way better than any alternatives so I just happily moved from lazydocs.

Cross-links from the main documentation to autogenerated from docstrings literals is just a wonderful feature.

@JP-Ellis
Copy link

Linking to mkdocstrings/griffe#256 as I raised a similar question.

In that issue, I draw inspiration from the way Rust handles re-exports in the documentation.

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

No branches or pull requests

3 participants