20

The package I am documenting consists of a set of *.py files, most containing one class with a couple of files being genuine modules with functions defined. I do not need to expose the fact that each class is in a module so I have added suitable from statements in the __init__.py file e.g.

from base import Base

so that the user can use the import pkg command and does not then have to specify the module that contains the class:

import pkg

class MyBase(pkg.Base):  # instead of pkg.base.Base ...
...

The problem is that Sphinx insists on documenting the class as pkg.base.Base. I have tried to set the add_module_names = False in conf.py. However this results in Sphinx showing the class as simply Base instead of pkg.Base. Additionally this also ruins the documentation of the couple of *.py files that are modules.

How do I make Sphinx show a class as pkg.Base? And how do I set the add_module_names directive selectively for each *.py file?

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Major Eccles
  • 491
  • 3
  • 8
  • 2
    Don't do that. Sphinx **correctly** tells the user where the class is *defined*, not where it is imported. If you import the `Base` class in two different modules how can Sphinx tell which name you want to use? If you don't want the user to know about the module where you define the class then you should probably make it private(which, if I remember correctly wont show in the generated files). – Bakuriu Feb 27 '13 at 15:33
  • 3
    @Bakuriu - Not sure I understand your comment. To clarify, the only reason that each class is in a separate file is to make it easier to manage vis-a-vis source code control. Usage of the classes only requires the package and class names and not the module name (which is redundant). Documentation should describe usage and not definition don't you think? How do you make the module name private? – Major Eccles Feb 28 '13 at 11:25
  • Then what's the problem? Just use `add_module_names = False` and place the documentation in the correct page. The users will see that in the page referring to module `pkg` there is a documented class `Base` and that's fine. The `pkg.module.Base` **only** refers to the module where the class is defined, not where you "use it"(which is something not well defined). If you want a dirty solution to obtain the output you want, then in `__init__` create a fake subclass: `class TheClass(pkg.module.TheClass): pass`, inheriting the documentation. – Bakuriu Feb 28 '13 at 12:24

4 Answers4

15

Here is a way to accomplish what the OP asks for:

  1. Add an __all__ list in pkg/__init__.py:

    from base import Base    # Or use 'from base import *'
    
    __all__ = ["Base"]
    
  2. Use .. automodule:: pkg in the .rst file.

Sphinx will now output documentation where the class name is shown as pkg.Base instead of pkg.base.Base.

mzjn
  • 48,958
  • 13
  • 128
  • 248
  • The problem with this is that if `Base` inherits from a baseclass also defined in `base`, the sphinx inheritance link will be broken. – Matt Sep 02 '20 at 20:15
  • 2
    Do you mean that "Bases" for `Base` is given as `pkg.base.ParentClass` instead of `pkg.ParentClass`? – mzjn Sep 03 '20 at 06:52
  • Does this help? https://stackoverflow.com/q/47903710/407651 – mzjn Sep 03 '20 at 14:36
  • 1
    Yeah, that's what I meant. I did see that but manipulating all the `__module__` from all the base-classes seemed too risky for me. Especially since some of that is cython code and I wonder if it'd break autodoc from being able to locate the source to parse. – Matt Sep 03 '20 at 23:49
  • Sphinx just outputs "pkg.Base: An alias of pkg.base.Base" without documenting anything about the `Base` class. – Ark-kun Aug 18 '23 at 07:44
1

I've incorporated the answers I found in a scalable-ish form factor:

my_project/
    __init__.py
    mess.py
  • mess.py:
class MyClass:
    pass

class MyOtherClass(MyClass):
    pass
  • __init__.py:
from .mess import MyClass, MyOtherClass

__all_exports = [MyClass, MyOtherClass]

for e in __all_exports:
    e.__module__ = __name__

__all__ = [e.__name__ for e in __all_exports]

This seems to have worked pretty well for me.

Micael Jarniac
  • 156
  • 1
  • 9
1

I would like to provide a more generalized approach.

The variable __all__ is filled up based on dir(). But the sub-packages name (here mypackage) and all in-build attributes (starting with __) are ignored.

from .mypackage import *

__all__ = []
for v in dir():
    if not v.startswith('__') and v != 'mypackage':
        __all__.append(v)
buhtz
  • 10,774
  • 18
  • 76
  • 149
-4

Short answer: You shouldn't. Just point the sphinx to the directory of your code. Sphinx documents the code and shows the module hirarchy. How the module finally will be imported is purely in the hand of the developer, but not a responsibility of the documentation tool.

mstuebner
  • 406
  • 2
  • 15