sphinx-autoapi/autoapi/documenters.py

298 lines
9.7 KiB
Python
Raw Normal View History

import re
2018-08-01 23:04:45 +00:00
from sphinx.ext import autodoc
from .mappers.python import (
PythonFunction,
PythonClass,
PythonMethod,
PythonData,
PythonAttribute,
PythonException,
)
2019-10-05 22:11:23 +00:00
# pylint: disable=attribute-defined-outside-init,no-self-use,unused-argument
2018-08-01 23:04:45 +00:00
2019-10-05 23:09:26 +00:00
2018-08-01 23:04:45 +00:00
class AutoapiDocumenter(autodoc.Documenter):
def get_attr(self, obj, name, *defargs):
attrgetters = self.env.app.registry.autodoc_attrgettrs
2018-08-01 23:04:45 +00:00
for type_, func in attrgetters.items():
if isinstance(obj, type_):
return func(obj, name, *defargs)
2019-01-27 05:20:45 +00:00
if name == "__doc__":
2018-08-01 23:04:45 +00:00
return obj.docstring
for child in obj.children:
if child.name == name:
return child
if defargs:
return defargs[0]
raise AttributeError(name)
def import_object(self):
2019-01-27 05:20:45 +00:00
max_splits = self.fullname.count(".")
for num_splits in range(max_splits, -1, -1):
2019-01-27 05:20:45 +00:00
path_stack = list(reversed(self.fullname.rsplit(".", num_splits)))
objects = self.env.autoapi_objects
2020-07-30 23:57:58 +00:00
parent = None
current = objects.get(path_stack.pop())
while current and path_stack:
parent = current
current = self.get_attr(current, path_stack.pop(), None)
if current:
self.object = current
self.object_name = current.name
self._method_parent = parent
return True
return False
2018-08-01 23:04:45 +00:00
def get_real_modname(self):
# Return a fake modname so that nothing can be imported
return None
def get_doc(self, encoding=None, ignore=1):
return [self.object.docstring.splitlines()]
def process_doc(self, docstrings):
for docstring in docstrings:
for line in docstring:
yield line
2019-01-27 05:20:45 +00:00
yield ""
2018-08-01 23:04:45 +00:00
def get_object_members(self, want_all):
children = ((child.name, child) for child in self.object.children)
if not want_all:
if not self.options.members:
return False, []
2019-01-27 05:20:45 +00:00
children = (child for child in children if child[0] in self.options.members)
elif not self.options.inherited_members:
children = (child for child in children if not child[1].inherited)
2018-08-01 23:04:45 +00:00
return False, sorted(children)
2019-10-05 22:11:23 +00:00
class _AutoapiDocstringSignatureMixin(object): # pylint: disable=too-few-public-methods
def format_signature(self, **kwargs):
# Set "manual" attributes at the last possible moment.
# This is to let a manual entry or docstring searching happen first,
# and falling back to the discovered signature only when necessary.
if self.args is None:
self.args = self.object.args
if self.retann is None:
self.retann = self.object.return_annotation
return super(_AutoapiDocstringSignatureMixin, self).format_signature(**kwargs)
class AutoapiFunctionDocumenter(
AutoapiDocumenter, autodoc.FunctionDocumenter, _AutoapiDocstringSignatureMixin
):
2019-01-27 05:20:45 +00:00
objtype = "apifunction"
directivetype = "function"
2018-08-01 23:04:45 +00:00
# Always prefer AutoapiDocumenters
priority = autodoc.FunctionDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonFunction)
2019-06-23 20:23:23 +00:00
def format_args(self, **kwargs):
2019-01-27 05:20:45 +00:00
return "(" + self.object.args + ")"
2018-08-01 23:04:45 +00:00
def add_directive_header(self, sig):
autodoc.Documenter.add_directive_header(self, sig)
if "async" in self.object.properties:
sourcename = self.get_sourcename()
self.add_line(" :async:", sourcename)
class AutoapiDecoratorDocumenter(
AutoapiFunctionDocumenter, AutoapiDocumenter, autodoc.DecoratorDocumenter
):
objtype = "apidecorator"
directivetype = "decorator"
priority = autodoc.DecoratorDocumenter.priority * 100 + 100
def format_signature(self, **kwargs):
if self.args is None:
self.args = self.format_args(**kwargs)
2018-08-01 23:04:45 +00:00
return super(AutoapiDecoratorDocumenter, self).format_signature(**kwargs)
def format_args(self, **kwargs):
to_format = self.object.args
if re.match(r"func\W", to_format) or to_format == "func":
if "," not in to_format:
return None
# We need to do better stripping here.
# An annotation with a comma will mess this up.
to_format = self.object.args.split(",", 1)[1]
return "(" + to_format + ")"
class AutoapiClassDocumenter(
AutoapiDocumenter, autodoc.ClassDocumenter, _AutoapiDocstringSignatureMixin
):
2019-01-27 05:20:45 +00:00
objtype = "apiclass"
directivetype = "class"
2018-08-01 23:04:45 +00:00
doc_as_attr = False
priority = autodoc.ClassDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonClass)
2019-06-23 20:23:23 +00:00
def format_args(self, **kwargs):
2019-01-27 05:20:45 +00:00
return "(" + self.object.args + ")"
2018-08-01 23:04:45 +00:00
def add_directive_header(self, sig):
autodoc.Documenter.add_directive_header(self, sig)
if self.options.show_inheritance:
sourcename = self.get_sourcename()
2019-01-27 05:20:45 +00:00
self.add_line(u"", sourcename)
2018-08-01 23:04:45 +00:00
# TODO: Change sphinx to allow overriding of getting base names
if self.object.bases:
2019-01-27 05:20:45 +00:00
bases = [":class:`{}`".format(base) for base in self.object.bases]
self.add_line(" " + "Bases: {}".format(", ".join(bases)), sourcename)
2018-08-01 23:04:45 +00:00
class AutoapiMethodDocumenter(
AutoapiDocumenter, autodoc.MethodDocumenter, _AutoapiDocstringSignatureMixin
):
2019-01-27 05:20:45 +00:00
objtype = "apimethod"
directivetype = "method"
priority = autodoc.MethodDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonMethod)
2019-06-23 20:23:23 +00:00
def format_args(self, **kwargs):
2019-01-27 05:20:45 +00:00
return "(" + self.object.args + ")"
2018-08-01 23:04:45 +00:00
def import_object(self):
result = super(AutoapiMethodDocumenter, self).import_object()
if result:
2020-07-30 23:57:58 +00:00
self.parent = self._method_parent
2019-01-27 05:20:45 +00:00
if self.object.method_type != "method":
2018-08-01 23:04:45 +00:00
# document class and static members before ordinary ones
self.member_order = self.member_order - 1
return result
2019-06-23 20:23:23 +00:00
def add_directive_header(self, sig):
autodoc.Documenter.add_directive_header(self, sig)
sourcename = self.get_sourcename()
for property_type in (
"abstractmethod",
"async",
"classmethod",
"staticmethod",
):
if property_type in self.object.properties:
self.add_line(" :{}:".format(property_type), sourcename)
2019-06-23 20:23:23 +00:00
2018-08-01 23:04:45 +00:00
class AutoapiPropertyDocumenter(
AutoapiMethodDocumenter, AutoapiDocumenter, autodoc.PropertyDocumenter
):
objtype = "apiproperty"
directivetype = "method"
# Always prefer AutoapiDocumenters
priority = autodoc.MethodDocumenter.priority * 100 + 100 + 1
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonMethod) and "property" in member.properties
def add_directive_header(self, sig):
super(AutoapiPropertyDocumenter, self).add_directive_header(sig)
sourcename = self.get_sourcename()
self.add_line(" :property:", sourcename)
2018-08-01 23:04:45 +00:00
class AutoapiDataDocumenter(AutoapiDocumenter, autodoc.DataDocumenter):
2019-01-27 05:20:45 +00:00
objtype = "apidata"
directivetype = "data"
priority = autodoc.DataDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonData)
def add_directive_header(self, sig):
autodoc.ModuleLevelDocumenter.add_directive_header(self, sig)
sourcename = self.get_sourcename()
if not self.options.annotation:
# TODO: Change sphinx to allow overriding of object description
if self.object.value is not None:
self.add_line(
2019-01-27 05:20:45 +00:00
" :annotation: = {}".format(self.object.value), sourcename
2018-08-01 23:04:45 +00:00
)
elif self.options.annotation is autodoc.SUPPRESS:
pass
else:
2019-01-27 05:20:45 +00:00
self.add_line(" :annotation: %s" % self.options.annotation, sourcename)
2018-08-01 23:04:45 +00:00
class AutoapiAttributeDocumenter(AutoapiDocumenter, autodoc.AttributeDocumenter):
2019-01-27 05:20:45 +00:00
objtype = "apiattribute"
directivetype = "attribute"
2018-08-01 23:04:45 +00:00
_datadescriptor = True
priority = autodoc.AttributeDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonAttribute)
def add_directive_header(self, sig):
autodoc.ClassLevelDocumenter.add_directive_header(self, sig)
sourcename = self.get_sourcename()
if not self.options.annotation:
# TODO: Change sphinx to allow overriding of object description
if self.object.value is not None:
self.add_line(
2019-01-27 05:20:45 +00:00
" :annotation: = {}".format(self.object.value), sourcename
2018-08-01 23:04:45 +00:00
)
elif self.options.annotation is autodoc.SUPPRESS:
pass
else:
2019-01-27 05:20:45 +00:00
self.add_line(" :annotation: %s" % self.options.annotation, sourcename)
2018-08-01 23:04:45 +00:00
class AutoapiModuleDocumenter(AutoapiDocumenter, autodoc.ModuleDocumenter):
2019-01-27 05:20:45 +00:00
objtype = "apimodule"
directivetype = "module"
priority = autodoc.ModuleDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
class AutoapiExceptionDocumenter(
2019-01-27 05:20:45 +00:00
AutoapiClassDocumenter, AutoapiDocumenter, autodoc.ExceptionDocumenter
):
objtype = "apiexception"
directivetype = "exception"
priority = autodoc.ExceptionDocumenter.priority * 100 + 100
2018-08-01 23:04:45 +00:00
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, PythonException)