# -*- coding: utf-8 -*- """ sphinx.ext.autodoc ~~~~~~~~~~~~~~~~~~ Automatically insert docstrings for functions, classes or whole modules into the doctree, thus avoiding duplication between docstrings and documentation for those who like elaborate docstrings. :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import re import sys import inspect import traceback from types import FunctionType, BuiltinFunctionType, MethodType from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, \ string_types, StringIO from docutils import nodes from docutils.utils import assemble_option_dict from docutils.statemachine import ViewList import sphinx from sphinx.util import rpartition, force_decode from sphinx.locale import _ from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.application import ExtensionError from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ safe_getattr, object_description, is_builtin_class_method from sphinx.util.docstrings import prepare_docstring try: import typing except ImportError: typing = None #: extended signature RE: with explicit module name separated by :: py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name ([\w.]+\.)? # module and/or class name(s) (\w+) \s* # thing name (?: \((.*)\) # optional: arguments (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) class DefDict(dict): """A dict that returns a default on nonexisting keys.""" def __init__(self, default): dict.__init__(self) self.default = default def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self.default def __bool__(self): # docutils check "if option_spec" return True __nonzero__ = __bool__ # for python2 compatibility def identity(x): return x class Options(dict): """A dict/attribute hybrid that returns None on nonexisting keys.""" def __getattr__(self, name): try: return self[name.replace('_', '-')] except KeyError: return None class _MockModule(object): """Used by autodoc_mock_imports.""" def __init__(self, *args, **kwargs): pass def __call__(self, *args, **kwargs): return _MockModule() @classmethod def __getattr__(cls, name): if name in ('__file__', '__path__'): return '/dev/null' elif name[0] == name[0].upper(): # Not very good, we assume Uppercase names are classes... mocktype = type(name, (), {}) mocktype.__module__ = __name__ return mocktype else: return _MockModule() def mock_import(modname): if '.' in modname: pkg, _n, mods = modname.rpartition('.') mock_import(pkg) mod = _MockModule() sys.modules[modname] = mod return mod ALL = object() INSTANCEATTR = object() def members_option(arg): """Used to convert the :members: option to auto directives.""" if arg is None: return ALL return [x.strip() for x in arg.split(',')] def members_set_option(arg): """Used to convert the :members: option to auto directives.""" if arg is None: return ALL return set(x.strip() for x in arg.split(',')) SUPPRESS = object() def annotation_option(arg): if arg is None: # suppress showing the representation of the object return SUPPRESS else: return arg def bool_option(arg): """Used to convert flag options to auto directives. (Instead of directives.flag(), which returns None). """ return True class AutodocReporter(object): """ A reporter replacement that assigns the correct source name and line number to a system message, as recorded in a ViewList. """ def __init__(self, viewlist, reporter): self.viewlist = viewlist self.reporter = reporter def __getattr__(self, name): return getattr(self.reporter, name) def system_message(self, level, message, *children, **kwargs): if 'line' in kwargs and 'source' not in kwargs: try: source, line = self.viewlist.items[kwargs['line']] except IndexError: pass else: kwargs['source'] = source kwargs['line'] = line return self.reporter.system_message(level, message, *children, **kwargs) def debug(self, *args, **kwargs): if self.reporter.debug_flag: return self.system_message(0, *args, **kwargs) def info(self, *args, **kwargs): return self.system_message(1, *args, **kwargs) def warning(self, *args, **kwargs): return self.system_message(2, *args, **kwargs) def error(self, *args, **kwargs): return self.system_message(3, *args, **kwargs) def severe(self, *args, **kwargs): return self.system_message(4, *args, **kwargs) # Some useful event listener factories for autodoc-process-docstring. def cut_lines(pre, post=0, what=None): """Return a listener that removes the first *pre* and last *post* lines of every docstring. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. Use like this (e.g. in the ``setup()`` function of :file:`conf.py`):: from sphinx.ext.autodoc import cut_lines app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) This can (and should) be used in place of :confval:`automodule_skip_lines`. """ def process(app, what_, name, obj, options, lines): if what and what_ not in what: return del lines[:pre] if post: # remove one trailing blank line. if lines and not lines[-1]: lines.pop(-1) del lines[-post:] # make sure there is a blank line at the end if lines and lines[-1]: lines.append('') return process def between(marker, what=None, keepempty=False, exclude=False): """Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made unless *keepempty* is true. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. """ marker_re = re.compile(marker) def process(app, what_, name, obj, options, lines): if what and what_ not in what: return deleted = 0 delete = not exclude orig_lines = lines[:] for i, line in enumerate(orig_lines): if delete: lines.pop(i - deleted) deleted += 1 if marker_re.match(line): delete = not delete if delete: lines.pop(i - deleted) deleted += 1 if not lines and not keepempty: lines[:] = orig_lines # make sure there is a blank line at the end if lines and lines[-1]: lines.append('') return process def format_annotation(annotation): """Return formatted representation of a type annotation. Show qualified names for types and additional details for types from the ``typing`` module. Displaying complex types from ``typing`` relies on its private API. """ qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ if annotation else repr(annotation)) if not isinstance(annotation, type): return repr(annotation) elif annotation.__module__ == 'builtins': return annotation.__qualname__ elif typing: if isinstance(annotation, typing.TypeVar): return annotation.__name__ elif hasattr(typing, 'GenericMeta') and \ isinstance(annotation, typing.GenericMeta) and \ hasattr(annotation, '__parameters__'): params = annotation.__parameters__ if params is not None: param_str = ', '.join(format_annotation(p) for p in params) return '%s[%s]' % (qualified_name, param_str) elif hasattr(typing, 'UnionMeta') and \ isinstance(annotation, typing.UnionMeta) and \ hasattr(annotation, '__union_params__'): params = annotation.__union_params__ if params is not None: param_str = ', '.join(format_annotation(p) for p in params) return '%s[%s]' % (qualified_name, param_str) elif hasattr(typing, 'CallableMeta') and \ isinstance(annotation, typing.CallableMeta) and \ hasattr(annotation, '__args__') and \ hasattr(annotation, '__result__'): args = annotation.__args__ if args is Ellipsis: args_str = '...' else: formatted_args = (format_annotation(a) for a in args) args_str = '[%s]' % ', '.join(formatted_args) return '%s[%s, %s]' % (qualified_name, args_str, format_annotation(annotation.__result__)) elif hasattr(typing, 'TupleMeta') and \ isinstance(annotation, typing.TupleMeta) and \ hasattr(annotation, '__tuple_params__') and \ hasattr(annotation, '__tuple_use_ellipsis__'): params = annotation.__tuple_params__ if params is not None: param_strings = [format_annotation(p) for p in params] if annotation.__tuple_use_ellipsis__: param_strings.append('...') return '%s[%s]' % (qualified_name, ', '.join(param_strings)) return qualified_name def formatargspec(function, args, varargs=None, varkw=None, defaults=None, kwonlyargs=(), kwonlydefaults={}, annotations={}): """Return a string representation of an ``inspect.FullArgSpec`` tuple. An enhanced version of ``inspect.formatargspec()`` that handles typing annotations better. """ def format_arg_with_annotation(name): if name in annotations: return '%s: %s' % (name, format_annotation(get_annotation(name))) return name def get_annotation(name): value = annotations[name] if isinstance(value, string_types): return introspected_hints.get(name, value) else: return value introspected_hints = (typing.get_type_hints(function) if typing and hasattr(function, '__code__') else {}) fd = StringIO() fd.write('(') formatted = [] defaults_start = len(args) - len(defaults) if defaults else len(args) for i, arg in enumerate(args): arg_fd = StringIO() arg_fd.write(format_arg_with_annotation(arg)) if defaults and i >= defaults_start: arg_fd.write(' = ' if arg in annotations else '=') arg_fd.write(object_description(defaults[i - defaults_start])) formatted.append(arg_fd.getvalue()) if varargs: formatted.append('*' + format_arg_with_annotation(varargs)) if kwonlyargs: formatted.append('*') for kwarg in kwonlyargs: arg_fd = StringIO() arg_fd.write(format_arg_with_annotation(kwarg)) if kwonlydefaults and kwarg in kwonlydefaults: arg_fd.write(' = ' if kwarg in annotations else '=') arg_fd.write(object_description(kwonlydefaults[kwarg])) formatted.append(arg_fd.getvalue()) if varkw: formatted.append('**' + format_arg_with_annotation(varkw)) fd.write(', '.join(formatted)) fd.write(')') if 'return' in annotations: fd.write(' -> ') fd.write(format_annotation(get_annotation('return'))) return fd.getvalue() class Documenter(object): """ A Documenter knows how to autodocument a single object type. When registered with the AutoDirective, it will be used to document objects of that type when needed by autodoc. Its *objtype* attribute selects what auto directive it is assigned to (the directive name is 'auto' + objtype), and what directive it generates by default, though that can be overridden by an attribute called *directivetype*. A Documenter has an *option_spec* that works like a docutils directive's; in fact, it will be used to parse an auto directive's options that matches the documenter. """ #: name by which the directive is called (auto...) and the default #: generated directive name objtype = 'object' #: indentation by which to indent the directive content content_indent = u' ' #: priority if multiple documenters return True from can_document_member priority = 0 #: order if autodoc_member_order is set to 'groupwise' member_order = 0 #: true if the generated content may contain titles titles_allowed = False option_spec = {'noindex': bool_option} @staticmethod def get_attr(obj, name, *defargs): """getattr() override for types such as Zope interfaces.""" for typ, func in iteritems(AutoDirective._special_attrgetters): if isinstance(obj, typ): return func(obj, name, *defargs) return safe_getattr(obj, name, *defargs) @classmethod def can_document_member(cls, member, membername, isattr, parent): """Called to see if a member can be documented by this documenter.""" raise NotImplementedError('must be implemented in subclasses') def __init__(self, directive, name, indent=u''): self.directive = directive self.env = directive.env self.options = directive.genopt self.name = name self.indent = indent # the module and object path within the module, and the fully # qualified name (all set after resolve_name succeeds) self.modname = None self.module = None self.objpath = None self.fullname = None # extra signature items (arguments and return annotation, # also set after resolve_name succeeds) self.args = None self.retann = None # the object to document (set after import_object succeeds) self.object = None self.object_name = None # the parent/owner of the object to document self.parent = None # the module analyzer to get at attribute docs, or None self.analyzer = None def add_line(self, line, source, *lineno): """Append one line of generated reST to the output.""" self.directive.result.append(self.indent + line, source, *lineno) def resolve_name(self, modname, parents, path, base): """Resolve the module and name of the object to document given by the arguments and the current module/class. Must return a pair of the module name and a chain of attributes; for example, it would return ``('zipfile', ['ZipFile', 'open'])`` for the ``zipfile.ZipFile.open`` method. """ raise NotImplementedError('must be implemented in subclasses') def parse_name(self): """Determine what module to import and what attribute to document. Returns True and sets *self.modname*, *self.objpath*, *self.fullname*, *self.args* and *self.retann* if parsing and resolving was successful. """ # first, parse the definition -- auto directives for classes and # functions can contain a signature which is then used instead of # an autogenerated one try: explicit_modname, path, base, args, retann = \ py_ext_sig_re.match(self.name).groups() except AttributeError: self.directive.warn('invalid signature for auto%s (%r)' % (self.objtype, self.name)) return False # support explicit module and class name separation via :: if explicit_modname is not None: modname = explicit_modname[:-2] parents = path and path.rstrip('.').split('.') or [] else: modname = None parents = [] self.modname, self.objpath = \ self.resolve_name(modname, parents, path, base) if not self.modname: return False self.args = args self.retann = retann self.fullname = (self.modname or '') + \ (self.objpath and '.' + '.'.join(self.objpath) or '') return True def import_object(self): """Import the object given by *self.modname* and *self.objpath* and set it as *self.object*. Returns True if successful, False if an error occurred. """ dbg = self.env.app.debug if self.objpath: dbg('[autodoc] from %s import %s', self.modname, '.'.join(self.objpath)) try: dbg('[autodoc] import %s', self.modname) for modname in self.env.config.autodoc_mock_imports: dbg('[autodoc] adding a mock module %s!', self.modname) mock_import(modname) __import__(self.modname) parent = None obj = self.module = sys.modules[self.modname] dbg('[autodoc] => %r', obj) for part in self.objpath: parent = obj dbg('[autodoc] getattr(_, %r)', part) obj = self.get_attr(obj, part) dbg('[autodoc] => %r', obj) self.object_name = part self.parent = parent self.object = obj return True # this used to only catch SyntaxError, ImportError and AttributeError, # but importing modules with side effects can raise all kinds of errors except (Exception, SystemExit) as e: if self.objpath: errmsg = 'autodoc: failed to import %s %r from module %r' % \ (self.objtype, '.'.join(self.objpath), self.modname) else: errmsg = 'autodoc: failed to import %s %r' % \ (self.objtype, self.fullname) if isinstance(e, SystemExit): errmsg += ('; the module executes module level statement ' + 'and it might call sys.exit().') else: errmsg += '; the following exception was raised:\n%s' % \ traceback.format_exc() if PY2: errmsg = errmsg.decode('utf-8') dbg(errmsg) self.directive.warn(errmsg) self.env.note_reread() return False def get_real_modname(self): """Get the real module name of an object to document. It can differ from the name of the module through which the object was imported. """ return self.get_attr(self.object, '__module__', None) or self.modname def check_module(self): """Check if *self.object* is really defined in the module given by *self.modname*. """ if self.options.imported_members: return True modname = self.get_attr(self.object, '__module__', None) if modname and modname != self.modname: return False return True def format_args(self): """Format the argument signature of *self.object*. Should return None if the object does not have a signature. """ return None def format_name(self): """Format the name of *self.object*. This normally should be something that can be parsed by the generated directive, but doesn't need to be (Sphinx will display it unparsed then). """ # normally the name doesn't contain the module (except for module # directives of course) return '.'.join(self.objpath) or self.modname def format_signature(self): """Format the signature (arguments and return annotation) of the object. Let the user process it via the ``autodoc-process-signature`` event. """ if self.args is not None: # signature given explicitly args = "(%s)" % self.args else: # try to introspect the signature try: args = self.format_args() except Exception as err: self.directive.warn('error while formatting arguments for ' '%s: %s' % (self.fullname, err)) args = None retann = self.retann result = self.env.app.emit_firstresult( 'autodoc-process-signature', self.objtype, self.fullname, self.object, self.options, args, retann) if result: args, retann = result if args is not None: return args + (retann and (' -> %s' % retann) or '') else: return '' def add_directive_header(self, sig): """Add the directive header and options to the generated content.""" domain = getattr(self, 'domain', 'py') directive = getattr(self, 'directivetype', self.objtype) name = self.format_name() sourcename = self.get_sourcename() self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig), sourcename) if self.options.noindex: self.add_line(u' :noindex:', sourcename) if self.objpath: # Be explicit about the module, this is necessary since .. class:: # etc. don't support a prepended module name self.add_line(u' :module: %s' % self.modname, sourcename) def get_doc(self, encoding=None, ignore=1): """Decode and return lines of the docstring(s) for the object.""" docstring = self.get_attr(self.object, '__doc__', None) # make sure we have Unicode docstrings, then sanitize and split # into lines if isinstance(docstring, text_type): return [prepare_docstring(docstring, ignore)] elif isinstance(docstring, str): # this will not trigger on Py3 return [prepare_docstring(force_decode(docstring, encoding), ignore)] # ... else it is something strange, let's ignore it return [] def process_doc(self, docstrings): """Let the user process the docstrings before adding them.""" for docstringlines in docstrings: if self.env.app: # let extensions preprocess docstrings self.env.app.emit('autodoc-process-docstring', self.objtype, self.fullname, self.object, self.options, docstringlines) for line in docstringlines: yield line def get_sourcename(self): if self.analyzer: # prevent encoding errors when the file name is non-ASCII if not isinstance(self.analyzer.srcname, text_type): filename = text_type(self.analyzer.srcname, sys.getfilesystemencoding(), 'replace') else: filename = self.analyzer.srcname return u'%s:docstring of %s' % (filename, self.fullname) return u'docstring of %s' % self.fullname def add_content(self, more_content, no_docstring=False): """Add content from docstrings, attribute documentation and user.""" # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: attr_docs = self.analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: no_docstring = True docstrings = [attr_docs[key]] for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings if not no_docstring: encoding = self.analyzer and self.analyzer.encoding docstrings = self.get_doc(encoding) if not docstrings: # append at least a dummy docstring, so that the event # autodoc-process-docstring is fired and can add some # content if desired docstrings.append([]) for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) def get_object_members(self, want_all): """Return `(members_check_module, members)` where `members` is a list of `(membername, member)` pairs of the members of *self.object*. If *want_all* is True, return all members. Else, only return those members given by *self.options.members* (which may also be none). """ analyzed_member_names = set() if self.analyzer: attr_docs = self.analyzer.find_attr_docs() namespace = '.'.join(self.objpath) for item in iteritems(attr_docs): if item[0][0] == namespace: analyzed_member_names.add(item[0][1]) if not want_all: if not self.options.members: return False, [] # specific members given members = [] for mname in self.options.members: try: members.append((mname, self.get_attr(self.object, mname))) except AttributeError: if mname not in analyzed_member_names: self.directive.warn('missing attribute %s in object %s' % (mname, self.fullname)) elif self.options.inherited_members: # safe_getmembers() uses dir() which pulls in members from all # base classes members = safe_getmembers(self.object, attr_getter=self.get_attr) else: # __dict__ contains only the members directly defined in # the class (but get them via getattr anyway, to e.g. get # unbound method objects instead of function objects); # using list(iterkeys()) because apparently there are objects for which # __dict__ changes while getting attributes try: obj_dict = self.get_attr(self.object, '__dict__') except AttributeError: members = [] else: members = [(mname, self.get_attr(self.object, mname, None)) for mname in list(iterkeys(obj_dict))] membernames = set(m[0] for m in members) # add instance attributes from the analyzer for aname in analyzed_member_names: if aname not in membernames and \ (want_all or aname in self.options.members): members.append((aname, INSTANCEATTR)) return False, sorted(members) def filter_members(self, members, want_all): """Filter the given member list. Members are skipped if - they are private (except if given explicitly or the private-members option is set) - they are special methods (except if given explicitly or the special-members option is set) - they are undocumented (except if the undoc-members option is set) The user can override the skipping decision by connecting to the ``autodoc-skip-member`` event. """ ret = [] # search for members in source code too namespace = '.'.join(self.objpath) # will be empty for modules if self.analyzer: attr_docs = self.analyzer.find_attr_docs() else: attr_docs = {} # process members and determine which to skip for (membername, member) in members: # if isattr is True, the member is documented as an attribute isattr = False doc = self.get_attr(member, '__doc__', None) # if the member __doc__ is the same as self's __doc__, it's just # inherited and therefore not the member's doc cls = self.get_attr(member, '__class__', None) if cls: cls_doc = self.get_attr(cls, '__doc__', None) if cls_doc == doc: doc = None has_doc = bool(doc) keep = False if want_all and membername.startswith('__') and \ membername.endswith('__') and len(membername) > 4: # special __methods__ if self.options.special_members is ALL and \ membername != '__doc__': keep = has_doc or self.options.undoc_members elif self.options.special_members and \ self.options.special_members is not ALL and \ membername in self.options.special_members: keep = has_doc or self.options.undoc_members elif want_all and membername.startswith('_'): # ignore members whose name starts with _ by default keep = self.options.private_members and \ (has_doc or self.options.undoc_members) elif (namespace, membername) in attr_docs: # keep documented attributes keep = True isattr = True else: # ignore undocumented members if :undoc-members: is not given keep = has_doc or self.options.undoc_members # give the user a chance to decide whether this member # should be skipped if self.env.app: # let extensions preprocess docstrings skip_user = self.env.app.emit_firstresult( 'autodoc-skip-member', self.objtype, membername, member, not keep, self.options) if skip_user is not None: keep = not skip_user if keep: ret.append((membername, member, isattr)) return ret def document_members(self, all_members=False): """Generate reST for member documentation. If *all_members* is True, do all members, else those given by *self.options.members*. """ # set current namespace for finding members self.env.temp_data['autodoc:module'] = self.modname if self.objpath: self.env.temp_data['autodoc:class'] = self.objpath[0] want_all = all_members or self.options.inherited_members or \ self.options.members is ALL # find out which members are documentable members_check_module, members = self.get_object_members(want_all) # remove members given by exclude-members if self.options.exclude_members: members = [(membername, member) for (membername, member) in members if membername not in self.options.exclude_members] # document non-skipped members memberdocumenters = [] for (mname, member, isattr) in self.filter_members(members, want_all): classes = [cls for cls in itervalues(AutoDirective._registry) if cls.can_document_member(member, mname, isattr, self)] if not classes: # don't know how to document this member continue # prefer the documenter with the highest priority classes.sort(key=lambda cls: cls.priority) # give explicitly separated module name, so that members # of inner classes can be documented full_mname = self.modname + '::' + \ '.'.join(self.objpath + [mname]) documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) member_order = self.options.member_order or \ self.env.config.autodoc_member_order if member_order == 'groupwise': # sort by group; relies on stable sort to keep items in the # same group sorted alphabetically memberdocumenters.sort(key=lambda e: e[0].member_order) elif member_order == 'bysource' and self.analyzer: # sort by source order, by virtue of the module analyzer tagorder = self.analyzer.tagorder def keyfunc(entry): fullname = entry[0].name.split('::')[1] return tagorder.get(fullname, len(tagorder)) memberdocumenters.sort(key=keyfunc) for documenter, isattr in memberdocumenters: documenter.generate( all_members=True, real_modname=self.real_modname, check_module=members_check_module and not isattr) # reset current objects self.env.temp_data['autodoc:module'] = None self.env.temp_data['autodoc:class'] = None def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): """Generate reST for the object given by *self.name*, and possibly for its members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is True, only generate if the object is defined in the module name it is imported from. If *all_members* is True, document all members. """ if not self.parse_name(): # need a module to import self.directive.warn( 'don\'t know which module to import for autodocumenting ' '%r (try placing a "module" or "currentmodule" directive ' 'in the document, or giving an explicit module name)' % self.name) return # now, import the module and get object to document if not self.import_object(): return # If there is no real module defined, figure out which to use. # The real module is used in the module analyzer to look up the module # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. self.real_modname = real_modname or self.get_real_modname() # try to also get a source code analyzer for attribute docs try: self.analyzer = ModuleAnalyzer.for_module(self.real_modname) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() except PycodeError as err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: self.directive.filename_set.add(self.module.__file__) else: self.directive.filename_set.add(self.analyzer.srcname) # check __module__ of object (for members not given explicitly) if check_module: if not self.check_module(): return sourcename = self.get_sourcename() # make sure that the result starts with an empty line. This is # necessary for some situations where another directive preprocesses # reST and no starting newline is present self.add_line(u'', sourcename) # format the object's signature, if any sig = self.format_signature() # generate the directive header and options, if applicable self.add_directive_header(sig) self.add_line(u'', sourcename) # e.g. the module directive doesn't have content self.indent += self.content_indent # add all content (from docstrings, attribute docs etc.) self.add_content(more_content) # document members, if possible self.document_members(all_members) class ModuleDocumenter(Documenter): """ Specialized Documenter subclass for modules. """ objtype = 'module' content_indent = u'' titles_allowed = True option_spec = { 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, 'member-order': identity, 'exclude-members': members_set_option, 'private-members': bool_option, 'special-members': members_option, 'imported-members': bool_option, } @classmethod def can_document_member(cls, member, membername, isattr, parent): # don't document submodules automatically return False def resolve_name(self, modname, parents, path, base): if modname is not None: self.directive.warn('"::" in automodule name doesn\'t make sense') return (path or '') + base, [] def parse_name(self): ret = Documenter.parse_name(self) if self.args or self.retann: self.directive.warn('signature arguments or return annotation ' 'given for automodule %s' % self.fullname) return ret def add_directive_header(self, sig): Documenter.add_directive_header(self, sig) sourcename = self.get_sourcename() # add some module-specific options if self.options.synopsis: self.add_line( u' :synopsis: ' + self.options.synopsis, sourcename) if self.options.platform: self.add_line( u' :platform: ' + self.options.platform, sourcename) if self.options.deprecated: self.add_line(u' :deprecated:', sourcename) def get_object_members(self, want_all): if want_all: if not hasattr(self.object, '__all__'): # for implicit module members, check __module__ to avoid # documenting imported objects return True, safe_getmembers(self.object) else: memberlist = self.object.__all__ # Sometimes __all__ is broken... if not isinstance(memberlist, (list, tuple)) or not \ all(isinstance(entry, string_types) for entry in memberlist): self.directive.warn( '__all__ should be a list of strings, not %r ' '(in module %s) -- ignoring __all__' % (memberlist, self.fullname)) # fall back to all members return True, safe_getmembers(self.object) else: memberlist = self.options.members or [] ret = [] for mname in memberlist: try: ret.append((mname, safe_getattr(self.object, mname))) except AttributeError: self.directive.warn( 'missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s' % ( safe_getattr(self.object, '__name__', '???'), mname)) return False, ret class ModuleLevelDocumenter(Documenter): """ Specialized Documenter subclass for objects on module level (functions, classes, data/constants). """ def resolve_name(self, modname, parents, path, base): if modname is None: if path: modname = path.rstrip('.') else: # if documenting a toplevel object without explicit module, # it can be contained in another auto directive ... modname = self.env.temp_data.get('autodoc:module') # ... or in the scope of a module directive if not modname: modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid return modname, parents + [base] class ClassLevelDocumenter(Documenter): """ Specialized Documenter subclass for objects on class level (methods, attributes). """ def resolve_name(self, modname, parents, path, base): if modname is None: if path: mod_cls = path.rstrip('.') else: mod_cls = None # if documenting a class-level object without path, # there must be a current class, either from a parent # auto directive ... mod_cls = self.env.temp_data.get('autodoc:class') # ... or from a class directive if mod_cls is None: mod_cls = self.env.ref_context.get('py:class') # ... if still None, there's no way to know if mod_cls is None: return None, [] modname, cls = rpartition(mod_cls, '.') parents = [cls] # if the module name is still missing, get it like above if not modname: modname = self.env.temp_data.get('autodoc:module') if not modname: modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid return modname, parents + [base] class DocstringSignatureMixin(object): """ Mixin for FunctionDocumenter and MethodDocumenter to provide the feature of reading the signature from the docstring. """ def _find_signature(self, encoding=None): docstrings = self.get_doc(encoding) self._new_docstrings = docstrings[:] result = None for i, doclines in enumerate(docstrings): # no lines in docstring, no match if not doclines: continue # match first line of docstring against signature RE match = py_ext_sig_re.match(doclines[0]) if not match: continue exmod, path, base, args, retann = match.groups() # the base name must match ours valid_names = [self.objpath[-1]] if isinstance(self, ClassDocumenter): valid_names.append('__init__') if hasattr(self.object, '__mro__'): valid_names.extend(cls.__name__ for cls in self.object.__mro__) if base not in valid_names: continue # re-prepare docstring to ignore more leading indentation self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[1:])) result = args, retann # don't look any further break return result def get_doc(self, encoding=None, ignore=1): lines = getattr(self, '_new_docstrings', None) if lines is not None: return lines return Documenter.get_doc(self, encoding, ignore) def format_signature(self): if self.args is None and self.env.config.autodoc_docstring_signature: # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() if result is not None: self.args, self.retann = result return Documenter.format_signature(self) class DocstringStripSignatureMixin(DocstringSignatureMixin): """ Mixin for AttributeDocumenter to provide the feature of stripping any function signature from the docstring. """ def format_signature(self): if self.args is None and self.env.config.autodoc_docstring_signature: # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() if result is not None: # Discarding _args is a only difference with # DocstringSignatureMixin.format_signature. # Documenter.format_signature use self.args value to format. _args, self.retann = result return Documenter.format_signature(self) class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for functions. """ objtype = 'function' member_order = 30 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, (FunctionType, BuiltinFunctionType)) def format_args(self): if inspect.isbuiltin(self.object) or \ inspect.ismethoddescriptor(self.object): # cannot introspect arguments of a C function or method return None try: argspec = getargspec(self.object) except TypeError: if (is_builtin_class_method(self.object, '__new__') and is_builtin_class_method(self.object, '__init__')): raise TypeError('%r is a builtin class' % self.object) # if a class should be documented as function (yay duck # typing) we try to use the constructor signature as function # signature without the first argument. try: argspec = getargspec(self.object.__new__) except TypeError: argspec = getargspec(self.object.__init__) if argspec[0]: del argspec[0][0] args = formatargspec(self.object, *argspec) # escape backslashes for reST args = args.replace('\\', '\\\\') return args def document_members(self, all_members=False): pass class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for classes. """ objtype = 'class' member_order = 20 option_spec = { 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'member-order': identity, 'exclude-members': members_set_option, 'private-members': bool_option, 'special-members': members_option, } @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, class_types) def import_object(self): ret = ModuleLevelDocumenter.import_object(self) # if the class is documented under another name, document it # as data/attribute if ret: if hasattr(self.object, '__name__'): self.doc_as_attr = (self.objpath[-1] != self.object.__name__) else: self.doc_as_attr = True return ret def format_args(self): # for classes, the relevant signature is the __init__ method's initmeth = self.get_attr(self.object, '__init__', None) # classes without __init__ method, default __init__ or # __init__ written in C? if initmeth is None or \ is_builtin_class_method(self.object, '__init__') or \ not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): return None try: argspec = getargspec(initmeth) except TypeError: # still not possible: happens e.g. for old-style classes # with __init__ in C return None if argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] return formatargspec(initmeth, *argspec) def format_signature(self): if self.doc_as_attr: return '' return DocstringSignatureMixin.format_signature(self) def add_directive_header(self, sig): if self.doc_as_attr: self.directivetype = 'attribute' Documenter.add_directive_header(self, sig) # add inheritance info, if wanted if not self.doc_as_attr and self.options.show_inheritance: sourcename = self.get_sourcename() self.add_line(u'', sourcename) if hasattr(self.object, '__bases__') and len(self.object.__bases__): bases = [b.__module__ in ('__builtin__', 'builtins') and u':class:`%s`' % b.__name__ or u':class:`%s.%s`' % (b.__module__, b.__name__) for b in self.object.__bases__] self.add_line(_(u' Bases: %s') % ', '.join(bases), sourcename) def get_doc(self, encoding=None, ignore=1): lines = getattr(self, '_new_docstrings', None) if lines is not None: return lines content = self.env.config.autoclass_content docstrings = [] attrdocstring = self.get_attr(self.object, '__doc__', None) if attrdocstring: docstrings.append(attrdocstring) # for classes, what the "docstring" is can be controlled via a # config value; the default is only the class docstring if content in ('both', 'init'): initdocstring = self.get_attr( self.get_attr(self.object, '__init__', None), '__doc__') # for new-style classes, no __init__ means default __init__ if (initdocstring is not None and (initdocstring == object.__init__.__doc__ or # for pypy initdocstring.strip() == object.__init__.__doc__)): # for !pypy initdocstring = None if not initdocstring: # try __new__ initdocstring = self.get_attr( self.get_attr(self.object, '__new__', None), '__doc__') # for new-style classes, no __new__ means default __new__ if (initdocstring is not None and (initdocstring == object.__new__.__doc__ or # for pypy initdocstring.strip() == object.__new__.__doc__)): # for !pypy initdocstring = None if initdocstring: if content == 'init': docstrings = [initdocstring] else: docstrings.append(initdocstring) doc = [] for docstring in docstrings: if isinstance(docstring, text_type): doc.append(prepare_docstring(docstring, ignore)) elif isinstance(docstring, str): # this will not trigger on Py3 doc.append(prepare_docstring(force_decode(docstring, encoding), ignore)) return doc def add_content(self, more_content, no_docstring=False): if self.doc_as_attr: classname = safe_getattr(self.object, '__name__', None) if classname: content = ViewList( [_('alias of :class:`%s`') % classname], source='') ModuleLevelDocumenter.add_content(self, content, no_docstring=True) else: ModuleLevelDocumenter.add_content(self, more_content) def document_members(self, all_members=False): if self.doc_as_attr: return ModuleLevelDocumenter.document_members(self, all_members) class ExceptionDocumenter(ClassDocumenter): """ Specialized ClassDocumenter subclass for exceptions. """ objtype = 'exception' member_order = 10 # needs a higher priority than ClassDocumenter priority = 10 @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, class_types) and \ issubclass(member, BaseException) class DataDocumenter(ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ objtype = 'data' member_order = 40 priority = -10 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(parent, ModuleDocumenter) and isattr def add_directive_header(self, sig): ModuleLevelDocumenter.add_directive_header(self, sig) sourcename = self.get_sourcename() if not self.options.annotation: try: objrepr = object_description(self.object) except ValueError: pass else: self.add_line(u' :annotation: = ' + objrepr, sourcename) elif self.options.annotation is SUPPRESS: pass else: self.add_line(u' :annotation: %s' % self.options.annotation, sourcename) def document_members(self, all_members=False): pass class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' member_order = 50 priority = 1 # must be more than FunctionDocumenter @classmethod def can_document_member(cls, member, membername, isattr, parent): return inspect.isroutine(member) and \ not isinstance(parent, ModuleDocumenter) def import_object(self): ret = ClassLevelDocumenter.import_object(self) if not ret: return ret # to distinguish classmethod/staticmethod obj = self.parent.__dict__.get(self.object_name) if isinstance(obj, classmethod): self.directivetype = 'classmethod' # document class and static members before ordinary ones self.member_order = self.member_order - 1 elif isinstance(obj, staticmethod): self.directivetype = 'staticmethod' # document class and static members before ordinary ones self.member_order = self.member_order - 1 else: self.directivetype = 'method' return ret def format_args(self): if inspect.isbuiltin(self.object) or \ inspect.ismethoddescriptor(self.object): # can never get arguments of a C function or method return None argspec = getargspec(self.object) if argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] args = formatargspec(self.object, *argspec) # escape backslashes for reST args = args.replace('\\', '\\\\') return args def document_members(self, all_members=False): pass class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for attributes. """ objtype = 'attribute' member_order = 60 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option # must be higher than the MethodDocumenter, else it will recognize # some non-data descriptors as methods priority = 10 method_types = (FunctionType, BuiltinFunctionType, MethodType) @classmethod def can_document_member(cls, member, membername, isattr, parent): isdatadesc = isdescriptor(member) and not \ isinstance(member, cls.method_types) and not \ type(member).__name__ in ("type", "method_descriptor", "instancemethod") return isdatadesc or (not isinstance(parent, ModuleDocumenter) and not inspect.isroutine(member) and not isinstance(member, class_types)) def document_members(self, all_members=False): pass def import_object(self): ret = ClassLevelDocumenter.import_object(self) if isdescriptor(self.object) and \ not isinstance(self.object, self.method_types): self._datadescriptor = True else: # if it's not a data descriptor self._datadescriptor = False return ret def get_real_modname(self): return self.get_attr(self.parent or self.object, '__module__', None) \ or self.modname def add_directive_header(self, sig): ClassLevelDocumenter.add_directive_header(self, sig) sourcename = self.get_sourcename() if not self.options.annotation: if not self._datadescriptor: try: objrepr = object_description(self.object) except ValueError: pass else: self.add_line(u' :annotation: = ' + objrepr, sourcename) elif self.options.annotation is SUPPRESS: pass else: self.add_line(u' :annotation: %s' % self.options.annotation, sourcename) def add_content(self, more_content, no_docstring=False): if not self._datadescriptor: # if it's not a data descriptor, its docstring is very probably the # wrong thing to display no_docstring = True ClassLevelDocumenter.add_content(self, more_content, no_docstring) class InstanceAttributeDocumenter(AttributeDocumenter): """ Specialized Documenter subclass for attributes that cannot be imported because they are instance attributes (e.g. assigned in __init__). """ objtype = 'instanceattribute' directivetype = 'attribute' member_order = 60 # must be higher than AttributeDocumenter priority = 11 @classmethod def can_document_member(cls, member, membername, isattr, parent): """This documents only INSTANCEATTR members.""" return isattr and (member is INSTANCEATTR) def import_object(self): """Never import anything.""" # disguise as an attribute self.objtype = 'attribute' self._datadescriptor = False return True def add_content(self, more_content, no_docstring=False): """Never try to get a docstring from the object.""" AttributeDocumenter.add_content(self, more_content, no_docstring=True) class AutoDirective(Directive): """ The AutoDirective class is used for all autodoc directives. It dispatches most of the work to one of the Documenters, which it selects through its *_registry* dictionary. The *_special_attrgetters* attribute is used to customize ``getattr()`` calls that the Documenters make; its entries are of the form ``type: getattr_function``. Note: When importing an object, all items along the import chain are accessed using the descendant's *_special_attrgetters*, thus this dictionary should include all necessary functions for accessing attributes of the parents. """ # a registry of objtype -> documenter class _registry = {} # a registry of type -> getattr function _special_attrgetters = {} # flags that can be given in autodoc_default_flags _default_flags = set([ 'members', 'undoc-members', 'inherited-members', 'show-inheritance', 'private-members', 'special-members', ]) # standard docutils directive settings has_content = True required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True # allow any options to be passed; the options are parsed further # by the selected Documenter option_spec = DefDict(identity) def warn(self, msg): self.warnings.append(self.reporter.warning(msg, line=self.lineno)) def run(self): self.filename_set = set() # a set of dependent filenames self.reporter = self.state.document.reporter self.env = self.state.document.settings.env self.warnings = [] self.result = ViewList() try: source, lineno = self.reporter.get_source_and_line(self.lineno) except AttributeError: source = lineno = None self.env.app.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text) # find out what documenter to call objtype = self.name[4:] doc_class = self._registry[objtype] # add default flags for flag in self._default_flags: if flag not in doc_class.option_spec: continue negated = self.options.pop('no-' + flag, 'not given') is None if flag in self.env.config.autodoc_default_flags and \ not negated: self.options[flag] = None # process the options with the selected documenter's option_spec try: self.genopt = Options(assemble_option_dict( self.options.items(), doc_class.option_spec)) except (KeyError, ValueError, TypeError) as err: # an option is either unknown or has a wrong type msg = self.reporter.error('An option to %s is either unknown or ' 'has an invalid value: %s' % (self.name, err), line=self.lineno) return [msg] # generate the output documenter = doc_class(self, self.arguments[0]) documenter.generate(more_content=self.content) if not self.result: return self.warnings self.env.app.debug2('[autodoc] output:\n%s', '\n'.join(self.result)) # record all filenames as dependencies -- this will at least # partially make automatic invalidation possible for fn in self.filename_set: self.state.document.settings.record_dependencies.add(fn) # use a custom reporter that correctly assigns lines to source # filename/description and lineno old_reporter = self.state.memo.reporter self.state.memo.reporter = AutodocReporter(self.result, self.state.memo.reporter) if documenter.titles_allowed: node = nodes.section() # necessary so that the child nodes get the right source/line set node.document = self.state.document nested_parse_with_titles(self.state, self.result, node) else: node = nodes.paragraph() node.document = self.state.document self.state.nested_parse(self.result, 0, node) self.state.memo.reporter = old_reporter return self.warnings + node.children def add_documenter(cls): """Register a new Documenter.""" if not issubclass(cls, Documenter): raise ExtensionError('autodoc documenter %r must be a subclass ' 'of Documenter' % cls) # actually, it should be possible to override Documenters # if cls.objtype in AutoDirective._registry: # raise ExtensionError('autodoc documenter for %r is already ' # 'registered' % cls.objtype) AutoDirective._registry[cls.objtype] = cls def setup(app): app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(AttributeDocumenter) app.add_autodocumenter(InstanceAttributeDocumenter) app.add_config_value('autoclass_content', 'class', True) app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_config_value('autodoc_default_flags', [], True) app.add_config_value('autodoc_docstring_signature', True, True) app.add_config_value('autodoc_mock_imports', [], True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') return {'version': sphinx.__display_version__, 'parallel_read_safe': True} class testcls: """test doc string""" def __getattr__(self, x): return x def __setattr__(self, x, y): """Attr setter."""