from sphinx.ext import autodoc from .mappers.python import ( PythonFunction, PythonClass, PythonMethod, PythonData, PythonAttribute, PythonException, ) from . import utils class AutoapiDocumenter(autodoc.Documenter): def get_attr(self, obj, name, *defargs): if (hasattr(self.env.app, 'registry') and hasattr(self.env.app.registry, 'autodocattrgettrs')): attrgetters = self.env.app.registry.autodoc_attrgettrs else: attrgetters = autodoc.AutoDirective._special_attrgetters for type_, func in attrgetters.items(): if isinstance(obj, type_): return func(obj, name, *defargs) if name == '__doc__': 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): path_stack = list(reversed(self.fullname.split('.'))) objects = self.env.autoapi_mapper.objects parent = objects[utils.slugify(path_stack.pop())] while parent and path_stack: parent = self.get_attr(parent, path_stack.pop(), None) if not parent: return False self.object = parent self.object_name = parent.name return True 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 yield '' 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, [] children = ( child for child in children if child[0] in self.options.members ) return False, sorted(children) class AutoapiFunctionDocumenter(AutoapiDocumenter, autodoc.FunctionDocumenter): objtype = 'apifunction' directivetype = 'function' # Always prefer AutoapiDocumenters priority = autodoc.FunctionDocumenter.priority * 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonFunction) def format_args(self): return '(' + self.object.args + ')' class AutoapiClassDocumenter(AutoapiDocumenter, autodoc.ClassDocumenter): objtype = 'apiclass' directivetype = 'class' doc_as_attr = False priority = autodoc.ClassDocumenter.priority * 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonClass) def format_args(self): return '(' + self.object.args + ')' def add_directive_header(self, sig): autodoc.Documenter.add_directive_header(self, sig) if self.options.show_inheritance: sourcename = self.get_sourcename() self.add_line(u'', sourcename) # TODO: Change sphinx to allow overriding of getting base names if self.object.bases: bases = [ ':class:`{}`'.format(base) for base in self.object.bases ] self.add_line( ' ' + 'Bases: {}'.format(', '.join(bases)), sourcename, ) class AutoapiMethodDocumenter(AutoapiDocumenter, autodoc.MethodDocumenter): objtype = 'apimethod' directivetype = 'method' priority = autodoc.MethodDocumenter.priority * 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonMethod) def format_args(self): return '(' + self.object.args + ')' def import_object(self): result = super(AutoapiMethodDocumenter, self).import_object() if result: if self.object.method_type != 'method': self.directivetype = self.object.method_type # document class and static members before ordinary ones self.member_order = self.member_order - 1 return result class AutoapiDataDocumenter(AutoapiDocumenter, autodoc.DataDocumenter): objtype = 'apidata' directivetype = 'data' priority = autodoc.DataDocumenter.priority * 100 @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( ' :annotation: = {}'.format(self.object.value), sourcename, ) elif self.options.annotation is autodoc.SUPPRESS: pass else: self.add_line( ' :annotation: %s' % self.options.annotation, sourcename, ) class AutoapiAttributeDocumenter(AutoapiDocumenter, autodoc.AttributeDocumenter): objtype = 'apiattribute' directivetype = 'attribute' _datadescriptor = True priority = autodoc.AttributeDocumenter.priority * 100 @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( ' :annotation: = {}'.format(self.object.value), sourcename, ) elif self.options.annotation is autodoc.SUPPRESS: pass else: self.add_line( ' :annotation: %s' % self.options.annotation, sourcename, ) class AutoapiModuleDocumenter(AutoapiDocumenter, autodoc.ModuleDocumenter): objtype = 'apimodule' directivetype = 'module' priority = autodoc.ModuleDocumenter.priority * 100 class AutoapiExceptionDocumenter( AutoapiClassDocumenter, AutoapiDocumenter, autodoc.ExceptionDocumenter): objtype = 'apiexception' directivetype = 'exception' priority = autodoc.ExceptionDocumenter.priority * 100 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, PythonException)