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

display names relative to the package's namespace #4065

Closed
Aran-Fey opened this issue Sep 17, 2017 · 7 comments
Closed

display names relative to the package's namespace #4065

Aran-Fey opened this issue Sep 17, 2017 · 7 comments
Labels
extensions:autodoc type:enhancement enhance or introduce a new feature
Milestone

Comments

@Aran-Fey
Copy link

Aran-Fey commented Sep 17, 2017

Note: This post has been massively edited because it was originally based on a misunderstanding on my part. I hope it makes more sense now. Unfortunately I can't seem to edit the title.


Consider a project structured like this:

package/
    __init__.py
    module.py

__init__.py contains:

from .module import Class

module.py contains:

class Class:
    pass

The docs generated by autodoc look like this:

package package

Submodules
package.module module

class package.module.Class

  Bases: object

Module contents

This is very different from what the user is supposed to see. A user would import Class with from package import Class, not with from package.module import Class. The user doesn't need to know in which file Class was defined. From the user's perspective, Class is located in package, and module doesn't even exist. The docs would be a lot more helpful if they looked like this:

package module

Module contents

class Class

The relevant changes I made are the following:

  1. Class is listed directly in package rather than in package.module
  2. module isn't listed as a submodule, because it doesn't contain anything relevant

This accurately displays package's public interface; there is no useless non-information in this documentation. If autodoc could produce output like this, it would be a massive improvement.

@peterjc
Copy link
Contributor

peterjc commented Sep 20, 2017

Does it help if your __init__.py contain an __all__ declaration?:

from .module import Class
__all__ = ('Class')

I found this was needed when the module was private (leading underscore in filename), meaning Sphinx by default would ignore my module file.

@Aran-Fey
Copy link
Author

Aran-Fey commented Sep 20, 2017

@peterjc Thank you, that does indeed help. With an __all__ definition, the output is

class package.Class

This is a lot better than package.module.Class, but ideally the package. prefix would be dropped as well.

Thanks to your suggestion, I have realized that my original feature request was (in part) based on a misunderstanding - it's not Sphinx's fault that the class was listed as package.module.Class; it's autodoc's fault. I will edit my previous post so that it makes more sense.

@LourensVeen
Copy link

LourensVeen commented Oct 4, 2019

I have the same setup and the same issue, and found out that you can use
add_module_names = False
in your conf.py to get rid of the package. prefix.

However, I still have a remaining issue. I'm using type annotations in my code, and autodoc renders those correctly, but it uses package.module.Class rather than package.Class or just Class, probably because that's where the class was imported from, and it doesn't understand that package.Class is the canonical public name. Even with add_module_names = False, I get the full name, which I don't want to expose to the user. Any ideas on getting rid of the prefix?

@LourensVeen
Copy link

If anyone else is stuck on the same issue of having the internal (private) package.module.Class shown, I worked around it by catching autodoc-process-signature in my conf.py and removing the extraneous prefix:

def strip_signatures(app, what, name, obj, options, signature, return_annotation):
    sig = None                                                                  
    if signature is not None:                                                   
        sig = re.sub('mypackage\.[^.]*\.', '', signature)                           
                                                                                
    ret = None                                                                  
    if return_annotation is not None:                                           
        ret = re.sub('mypackage\.[^.]*\.', '', signature)                           
                                                                                
    return sig, ret                                                             
                                                                                
                                                                                
def setup(app):                                                                 
    app.connect('autodoc-process-signature', strip_signatures)

As written this only works one level deep, as that's all I needed. You'll want to tweak some I'm sure.

@tk0miya tk0miya added extensions:autodoc type:enhancement enhance or introduce a new feature labels Oct 5, 2019
@tk0miya tk0miya added this to the 3.0.0 milestone Oct 5, 2019
@tk0miya tk0miya modified the milestones: 3.0.0, 3.1.0 Mar 14, 2020
@tk0miya tk0miya modified the milestones: 3.1.0, 3.2.0 May 30, 2020
@tk0miya tk0miya modified the milestones: 3.2.0, 4.0.0 Jul 18, 2020
@dkfellows
Copy link

The core problem with base class documentation seems to be that sphinx.typing.restify doesn't seem to have any way to take a hint from anywhere what the public full name of a class should be; it just goes “I know what's going on here” and does its thing. If there was a way we could tell it directly “this is the correct name” that'd greatly reduce the horribleness.

I'm reluctant to use a post-processing string hack as I've got a lot of classes in many modules across many repositories, and getting such hacks right would be a lot of work. I'm also reluctant to alter the __module__ attribute directly as I don't know what other consumers that has. (Our code is definitely more complex than it ought to be in places!)

If there was a, say, __sphinx_api_name__ (to pick a name out of the air without thinking about it for ages) attribute on the classes that would be believed, that'd work very well; then people would at least have a way to get things correct for the classes that they control, and that way would definitely not interfere with anything else. (It'd be a candidate for fixing with a suitable decorator, but that'd be easy to arrange.)

@tk0miya
Copy link
Member

tk0miya commented Jan 18, 2021

@dkfellows I'm planning to define the "canonical" name to the class definition like the following:

.. py:class:: package.Class
   :canonical: package.module.Class

After that, other documents can refer to the class via both names.

At present, the :canonical: option has been implemented (refs: #7463). So the remaining task is modifying autodoc to generate it. I'll work on it until the next major version.

@tk0miya
Copy link
Member

tk0miya commented Mar 27, 2021

Now I merged #9026 that generates :canonical: option for the imported classes. I believe this issue is resolved now.
Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
extensions:autodoc type:enhancement enhance or introduce a new feature
Projects
None yet
Development

No branches or pull requests

5 participants