sphinx-doc / sphinx

The Sphinx documentation generator
https://www.sphinx-doc.org/
Other
6.55k stars 2.12k forks source link

sphinx-build should auto-generate autodoc/summary rst files that can be customized using a Python callback #6829

Open ben-spiller opened 4 years ago

ben-spiller commented 4 years ago

Is your feature request related to a problem? Please describe. I want to automatically and recursively generate documentation for my Python modules, using Python code in conf.py to customize the generated rst files for each module.

Currently there are two different (kindof competing/overlapping) mechanisms for auto-generating rsts, and both use jinja templates as the only way to customize the output. After a lot of time experimenting I found neither of them quite did the job:

In the end I had to write my own custom Python code to walk the modules and generate rst in the format I wanted (piggybacking on autodoc's filter_members() and ModuleAnalyzer code and autosummary's FakeDirective mechanism for accessing autodoc Documenter classes... all pretty complex!), which isn't ideal and probably will break in future sphinx releases.

Fundamentally, Sphinx already contains some powerful logic for iterating over a hierarchy of modules/classes and filtering out private/undocumented/user-defined members, so I'm proposing we expose that excellent functionality to conf.py authors for the cases not supported by the templating mechaism.

Describe the solution you'd like I propose we add a new extension dedicated to the task of automatically generating rts files for modules. This would be similar to autosummary_generate but:

The callback for generating .rst content would need a way to access Sphinx's functionality for enumerating, categorizing, filtering and extracting the docstring for modules, classes, etc (basically a cleaner version of what autosummary already does).

Could look like this:

def generate_module_rst(app, moduledoc: Documentable):
   # default implementation would be to delegate to a template (using the fields from moduledoc) similar to autosummary, but users could write custom Python via a config callable if they want to
  # user can return None if they wish to skip rst documentation for this module - that provides an easy way to implement arbitrary skipping rules

class Documentable:
   """Holds information about a documentable item such as a module, class, function, data. This is essentially a thin public API wrapper around the autodoc Documenter classes that exposes everything from Sphinx that someone might possibly want to use when generating rst content."""
   kind # function, class, module, data, etc
   docstring # because getting this for data/attribute is very hard and Sphinx can already do it
   qualname # fully qualified name
   obj # instance of object to be documented
   app # so we can get configuration info
  __documenter # private field holding the non-public autodoc XXXDocumenter class, which is needed for getting the members

   def get_members(self, filter=True): # get the members of this item e.g. for a module the (sub)modules/classes/data etc it contains
    # filter can be used to toggle autodoc's filtering of private/undoc/skip-member items

All the hard bits are already implemented in Sphinx... but hard to get at. The proposal is really just about some fairly modest wrappers to expose all that goodness to the person writing the conf.py file. I didn't find any other proposal that solves it in such a neat way, and gives full control to the person writing the conf.py file (without making them also reimplement things Sphinbx can already do).

This capability would provide a superset of what apidoc/autosummary_generate can do, and those could be easily implemented in terms of the more powerful primitives suggested here. By having some common code whether you want to generate autoXXX directives or autosummary directives, we avoid the need to keep dealing with bugs and feature requests from the diverging autodoc+autosummary implementations.

Describe alternatives you've considered I considered sketching an implementation out with a PR but didn't have time (at least right now), and thought it'd be best to float the general idea with you guys before investing any time.

I see lots of issues, stackoverflow posts and third-party sphinx extensions around this topic so think it could really help the sphinx community, and maybe provide a neater and more potent architectural solution to various other PRs and bugs underway e.g.

tk0miya commented 4 years ago

Thank you for proposal. I agree python-related feature of Sphinx is very complicated and confusable. And I still don't understand perfectly how they works. In addition, unfortunately, many extensions depends on current implementation of these modules. So we need to keep interfaces of them to keep compatibility. As a result, IMO, we need to scrap auto* modules and build new one like sphinx.ext.autodoc2.

goerz commented 4 years ago

I would think that #6768 should pretty much do whatever you need to do (my needs are very similar to yours). Of course, you're right that ultimately, nothing is more flexible than complete custom python code, but the (Python-implemented) get_members function in combination with all of Jinja's templating features is extremely flexible already. It will definitely allow you to categorize members. Also, get_members is able to process lists that you define at a module level. The __all__ list is standard, but personally I tend to also have a __private__ list, and you can define your own lists for categorization beyond what get_members can auto-detect.

goerz commented 4 years ago

There is also sphinxcontrib-apidoc which wraps apidoc from conf.py. Once #6768 is merged, I'll create a PR for that project to add explicit support for the templating flags.