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

Duplicated symbols appear in 2 places #317

Closed
Conchylicultor opened this issue Jan 7, 2022 · 7 comments
Closed

Duplicated symbols appear in 2 places #317

Conchylicultor opened this issue Jan 7, 2022 · 7 comments

Comments

@Conchylicultor
Copy link

I have a module defining a public API:

my_module/
  __init__.py
  implementation.py

In __init__.py:

from my_module.my_implementation import X

__all__ = [
    'X',
]

However, X is documented twice:

  • In autodoc/my_module/index
  • In autodoc/my_module/implementation/index

I think this is confusing for the end-user. When I define a public API in __init__.py, I would like to only document the top-level public API. autodoc/my_module/implementation should be considered internal implementation and never shown.

Similarly I would like the typing aliases should redirect to my_module.X:

def f(x: my_module.X):  # Currently `x: my_module.implementation.X`

Is it possible to do currently ? If so how ? I would be fine with a python hook that I can use to programatically program this behavior.

@Conchylicultor Conchylicultor changed the title Duplicated symbols Duplicated symbols documented twice Jan 7, 2022
@Conchylicultor Conchylicultor changed the title Duplicated symbols documented twice Duplicated symbols appear in 2 places Jan 7, 2022
@warsaw
Copy link

warsaw commented Jan 10, 2022

Same issue with my atpublic package. I even tried renaming my implementation files to _private.py and _public.py and importing from them into my __init__.py, then adding the underscore modules to autoapi_ignore but that only resulted in none of the functions getting documented.

@astrojuanlu
Copy link

Experiencing this in poliastro as well. The solution given in #262 (comment), which is related to this issue, was to "attach an event handler to the autoapi-skip-member event". However, it's not very ergonomic.

@AWhetter
Copy link
Collaborator

AWhetter commented Feb 9, 2022

AutoAPI chooses to document everything that is considered public by default. Therefore because your API makes the function available in both places, it is documented in both places. So to hide it in one place you either need do what @warsaw suggested and make it private in one place, or do what @astrojuanlu suggested and use autoapi-skip-member to hide what you don't want to be documented (note that you will also need to have "imported-members" set in autoapi_options, which it is by default, for this to work).

@AWhetter AWhetter closed this as completed Feb 9, 2022
@warsaw
Copy link

warsaw commented Feb 9, 2022

Thanks for the reply @AWhetter but I'm not actually sure this solves my problem with atpublic.

With my approach of using _public.py and _private.py as the implementation files, and importing from them in __init__.py:

from ._private import private
from ._public import public

if I don't set autoapi_ignore I see submodules public._private, public._public, and public.types. This doesn't change even when I add autoapi_ignore = ['_*.py']. Is this supposed to work or am I doing something wrong?

@warsaw
Copy link

warsaw commented Feb 9, 2022

Aha! Forget about autoapi_ignore and set autoapi_options to:

autoapi_options = [
    'members',
    'undoc-members',
#    'private-members',
    'show-inheritance',
    'show-module-summary',
    'special-members',
    'imported-members',
    ]

i.e. removing private-members does the trick (for this package at least).

@tony
Copy link

tony commented Jul 3, 2022

I am a bit perplexed by the answers here. 😆 I am creating #342. None of these seem like solutions and this is definitely feeling - from a user perspective - like a bug that'd break for most users

AutoAPI chooses to document everything that is considered public by default. Therefore because your API makes the function available in both places, it is documented in both places.

Thank you for the project and your time on the project @AWhetter.

  • In regards to "public":

    To me, "public" are the function/class declarations. They can only happen one time.

    When you say public, what's it mean?

    Depending on the user's expectation when documenting APIs or in a strictly technical sense, e.g. when you're recursing through module members while developing / maintaining sphinx-autoapi itself (which we thank you for)

  • Should imports not specified __all__ ever show in automodule directives?

@Conchylicultor
Copy link
Author

Conchylicultor commented Jul 3, 2022

I agree with @tony, I don't understand why this issue was closed.

Technically, everything is public in Python, but conventions have been defined to indicate which symbols are public or not:

  • At the file level: _symbol is just a convention to indicate a symbol should not be used outside of the file it is defined
  • At the package level (multiple files): __init__.py + __all__ is the convention used by all major package to indicate which symbol are public and which are internal at the package level (multiple files).
    (note that a symbol can be internal for the end user but still used across internal files, so cannot start by _).

This is how all major Python libraries are defined.

For example, if we take numpy, according to the autoapi authors, sin() should be documented 3 times !!!

np.sin
np.core.sin
np.core.umath.sin

However the numpy authors made it explicit that np.core is NOT part of the public API:

assert 'sin' in np.__all__
assert 'core' not in np.__all__

So sphinx-autoapi seems to violate this very standard Python convention.

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

5 participants