parent
3747596021
commit
076427e6ae
8 changed files with 191 additions and 3 deletions
|
|
@ -12,6 +12,11 @@ Features
|
|||
AutoAPI to search for implicit namespace packages.
|
||||
* Added support for Sphinx 2.2 and 2.3.
|
||||
* Added support for Python 3.8.
|
||||
* `#140 <https://github.com/readthedocs/sphinx-autoapi/issues/140>`: (Python)
|
||||
Added the ``autoapi-inheritance-diagram`` directive to create
|
||||
inheritance diagrams without importing modules.
|
||||
Enable the ``autoapi_include_inheritance_diagrams`` option to
|
||||
turn the diagrams on in generated documentation.
|
||||
|
||||
Bug Fixes
|
||||
^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from .backends import (
|
|||
LANGUAGE_REQUIREMENTS,
|
||||
)
|
||||
from .directives import AutoapiSummary, NestedParse
|
||||
from .inheritance_diagrams import AutoapiInheritanceDiagram
|
||||
from .settings import API_ROOT
|
||||
from .toctree import add_domain_to_toctree
|
||||
|
||||
|
|
@ -260,6 +261,7 @@ def setup(app):
|
|||
app.add_config_value("autoapi_keep_files", False, "html")
|
||||
app.add_config_value("autoapi_add_toctree_entry", True, "html")
|
||||
app.add_config_value("autoapi_template_dir", None, "html")
|
||||
app.add_config_value("autoapi_include_inheritance_graphs", False, "html")
|
||||
app.add_config_value("autoapi_include_summaries", False, "html")
|
||||
app.add_config_value("autoapi_python_use_implicit_namespaces", False, "html")
|
||||
app.add_config_value("autoapi_python_class_content", "class", "html")
|
||||
|
|
@ -277,3 +279,5 @@ def setup(app):
|
|||
directives.register_directive("autoapisummary", AutoapiSummary)
|
||||
app.setup_extension("sphinx.ext.autosummary")
|
||||
app.add_event("autoapi-skip-member")
|
||||
app.setup_extension("sphinx.ext.inheritance_diagram")
|
||||
app.add_directive("autoapi-inheritance-diagram", AutoapiInheritanceDiagram)
|
||||
|
|
|
|||
129
autoapi/inheritance_diagrams.py
Normal file
129
autoapi/inheritance_diagrams.py
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import sys
|
||||
|
||||
import astroid
|
||||
import sphinx.ext.inheritance_diagram
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
_BUILTINS = "builtins"
|
||||
else:
|
||||
_BUILTINS = "__builtins__"
|
||||
|
||||
|
||||
def _import_class(name, currmodule):
|
||||
path_stack = list(reversed(name.split(".")))
|
||||
target = None
|
||||
if currmodule:
|
||||
try:
|
||||
target = astroid.MANAGER.ast_from_module_name(currmodule)
|
||||
while target and path_stack:
|
||||
target = (target.getattr(path_stack.pop()) or (None,))[0]
|
||||
except astroid.AstroidError:
|
||||
target = None
|
||||
|
||||
if target is None:
|
||||
path_stack = list(reversed(name.split(".")))
|
||||
try:
|
||||
target = astroid.MANAGER.ast_from_module_name(path_stack.pop())
|
||||
while target and path_stack:
|
||||
target = (target.getattr(path_stack.pop()) or (None,))[0]
|
||||
except astroid.AstroidError:
|
||||
target = None
|
||||
|
||||
if not target:
|
||||
raise sphinx.ext.inheritance_diagram.InheritanceException(
|
||||
"Could not import class or module {} specified for inheritance diagram".format(
|
||||
name
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(target, astroid.ClassDef):
|
||||
return [target]
|
||||
|
||||
if isinstance(target, astroid.Module):
|
||||
classes = []
|
||||
for child in target.children:
|
||||
if isinstance(child, astroid.ClassDef):
|
||||
classes.append(child)
|
||||
return classes
|
||||
|
||||
raise sphinx.ext.inheritance_diagram.InheritanceException(
|
||||
"{} specified for inheritance diagram is not a class or module".format(name)
|
||||
)
|
||||
|
||||
|
||||
class _AutoapiInheritanceGraph(sphinx.ext.inheritance_diagram.InheritanceGraph):
|
||||
@staticmethod
|
||||
def _import_classes(class_names, currmodule):
|
||||
classes = []
|
||||
|
||||
for name in class_names:
|
||||
classes.extend(_import_class(name, currmodule))
|
||||
|
||||
return classes
|
||||
|
||||
def _class_info(
|
||||
self, classes, show_builtins, private_bases, parts, aliases, top_classes
|
||||
): # pylint: disable=too-many-arguments
|
||||
all_classes = {}
|
||||
|
||||
def recurse(cls):
|
||||
if cls in all_classes:
|
||||
return
|
||||
if not show_builtins and cls.root().name == _BUILTINS:
|
||||
return
|
||||
if not private_bases and cls.name.startswith("_"):
|
||||
return
|
||||
|
||||
nodename = self.class_name(cls, parts, aliases)
|
||||
fullname = self.class_name(cls, 0, aliases)
|
||||
|
||||
tooltip = None
|
||||
if cls.doc:
|
||||
doc = cls.doc.strip().split("\n")[0]
|
||||
if doc:
|
||||
tooltip = '"%s"' % doc.replace('"', '\\"')
|
||||
|
||||
baselist = []
|
||||
all_classes[cls] = (nodename, fullname, baselist, tooltip)
|
||||
|
||||
if fullname in top_classes:
|
||||
return
|
||||
|
||||
for base in cls.ancestors(recurs=False):
|
||||
if not show_builtins and base.root().name == _BUILTINS:
|
||||
continue
|
||||
if not private_bases and base.name.startswith("_"):
|
||||
continue
|
||||
baselist.append(self.class_name(base, parts, aliases))
|
||||
if base not in all_classes:
|
||||
recurse(base)
|
||||
|
||||
for cls in classes:
|
||||
recurse(cls)
|
||||
|
||||
return list(all_classes.values())
|
||||
|
||||
@staticmethod
|
||||
def class_name(node, parts=0, aliases=None):
|
||||
fullname = node.qname()
|
||||
if fullname.startswith(("__builtin__.", "builtins")):
|
||||
fullname = fullname.split(".", 1)[-1]
|
||||
if parts == 0:
|
||||
result = fullname
|
||||
else:
|
||||
name_parts = fullname.split(".")
|
||||
result = ".".join(name_parts[-parts:])
|
||||
if aliases is not None and result in aliases:
|
||||
return aliases[result]
|
||||
return result
|
||||
|
||||
|
||||
class AutoapiInheritanceDiagram(sphinx.ext.inheritance_diagram.InheritanceDiagram):
|
||||
def run(self):
|
||||
# Yucky! Monkeypatch InheritanceGraph to use our own
|
||||
old_graph = sphinx.ext.inheritance_diagram.InheritanceGraph
|
||||
sphinx.ext.inheritance_diagram.InheritanceGraph = _AutoapiInheritanceGraph
|
||||
try:
|
||||
return super(AutoapiInheritanceDiagram, self).run()
|
||||
finally:
|
||||
sphinx.ext.inheritance_diagram.InheritanceGraph = old_graph
|
||||
|
|
@ -85,7 +85,13 @@ class PythonMapperBase(object):
|
|||
@property
|
||||
def rendered(self):
|
||||
"""Shortcut to render an object in templates."""
|
||||
return self.render()
|
||||
return self.render(
|
||||
include_inheritance_graphs=self.app.config.autoapi_include_inheritance_graphs,
|
||||
include_private_inheritance=(
|
||||
"private-members" in self.app.config.autoapi_options
|
||||
),
|
||||
include_summaries=self.app.config.autoapi_include_summaries,
|
||||
)
|
||||
|
||||
def get_context_data(self):
|
||||
return {"obj": self, "sphinx_version": sphinx.version_info}
|
||||
|
|
@ -291,7 +297,11 @@ class SphinxMapperBase(object):
|
|||
stringify_func=(lambda x: x[0]),
|
||||
):
|
||||
rst = obj.render(
|
||||
include_summaries=self.app.config.autoapi_include_summaries
|
||||
include_inheritance_graphs=self.app.config.autoapi_include_inheritance_graphs,
|
||||
include_private_inheritance=(
|
||||
"private-members" in self.app.config.autoapi_options
|
||||
),
|
||||
include_summaries=self.app.config.autoapi_include_summaries,
|
||||
)
|
||||
if not rst:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@
|
|||
Bases: {% for base in obj.bases %}:class:`{{ base }}`{% if not loop.last %}, {% endif %}{% endfor %}
|
||||
|
||||
|
||||
{% if include_inheritance_graphs and obj.bases != ["object"] %}
|
||||
.. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }}
|
||||
:parts: 1
|
||||
{% if include_private_inheritance %}:private-bases:{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if obj.docstring %}
|
||||
{{ obj.docstring|prepare_docstring|indent(3) }}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ Customisation Options
|
|||
and you will need to include the generated documentation
|
||||
in a TOC tree entry yourself.
|
||||
|
||||
.. confval:: autoapi_include_inheritance_graphs
|
||||
|
||||
Defalut: ``False``
|
||||
|
||||
Whether to include inheritance diagrams in generated class documentation.
|
||||
This is a shortcut for needing to edit the templates yourself.
|
||||
It makes use of the :mod:`sphinx.ext.inheritance_diagram` extension,
|
||||
and requires `Graphviz <https://graphviz.org/>`_ to be installed.
|
||||
|
||||
.. confval:: autoapi_include_summaries
|
||||
|
||||
Default: ``False``
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
Directives
|
||||
==========
|
||||
|
||||
.. _autodoc-directives:
|
||||
|
||||
Autodoc-Style Directives
|
||||
========================
|
||||
------------------------
|
||||
|
||||
You can opt to write API documentation yourself using autodoc style directives.
|
||||
These directives work similarly to autodoc,
|
||||
|
|
@ -40,3 +43,24 @@ The following directives are available:
|
|||
|
||||
Equivalent to :rst:dir:`autofunction`, :rst:dir:`autodata`,
|
||||
:rst:dir:`automethod`, and :rst:dir:`autoattribute` respectively.
|
||||
|
||||
|
||||
Inheritance Diagrams
|
||||
--------------------
|
||||
|
||||
.. rst:directive:: autoapi-inheritance-diagram
|
||||
|
||||
This directive uses the :mod:`sphinx.ext.inheritance_diagram` extension
|
||||
to create inheritance diagrams for classes.
|
||||
|
||||
For example:
|
||||
|
||||
.. autoapi-inheritance-diagram:: autoapi.mappers.python.objects.PythonModule autoapi.mappers.python.objects.PythonPackage
|
||||
:parts: 1
|
||||
|
||||
:mod:`sphinx.ext.inheritance_diagram` makes use of the
|
||||
:mod:`sphinx.ext.graphviz` extension,
|
||||
and therefore it requires `Graphviz <https://graphviz.org/>`_ to be installed.
|
||||
|
||||
The directive can be configured using the same options as
|
||||
:mod:`sphinx.ext.inheritance_diagram`.
|
||||
|
|
|
|||
1
pylintrc
1
pylintrc
|
|
@ -11,6 +11,7 @@ disable=bad-continuation,
|
|||
missing-class-docstring,
|
||||
missing-function-docstring,
|
||||
missing-module-docstring,
|
||||
too-few-public-methods,
|
||||
too-many-locals,
|
||||
too-many-instance-attributes,
|
||||
useless-object-inheritance
|
||||
|
|
|
|||
Loading…
Reference in a new issue