mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add docstrings to autodoc.
This commit is contained in:
@@ -34,6 +34,7 @@ except NameError:
|
|||||||
base_exception = Exception
|
base_exception = Exception
|
||||||
|
|
||||||
|
|
||||||
|
#: extended signature RE: with explicit module name separated by ::
|
||||||
py_ext_sig_re = re.compile(
|
py_ext_sig_re = re.compile(
|
||||||
r'''^ ([\w.]+::)? # explicit module name
|
r'''^ ([\w.]+::)? # explicit module name
|
||||||
([\w.]+\.)? # module and/or class name(s)
|
([\w.]+\.)? # module and/or class name(s)
|
||||||
@@ -45,6 +46,7 @@ py_ext_sig_re = re.compile(
|
|||||||
|
|
||||||
|
|
||||||
class DefDict(dict):
|
class DefDict(dict):
|
||||||
|
"""A dict that returns a default on nonexisting keys."""
|
||||||
def __init__(self, default):
|
def __init__(self, default):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
self.default = default
|
self.default = default
|
||||||
@@ -61,21 +63,25 @@ identity = lambda x: x
|
|||||||
|
|
||||||
|
|
||||||
class Options(dict):
|
class Options(dict):
|
||||||
|
"""A dict/attribute hybrid that returns None on nonexisting keys."""
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
try:
|
try:
|
||||||
return self[name.replace('_', '-')]
|
return self[name.replace('_', '-')]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return None
|
||||||
|
|
||||||
|
|
||||||
ALL = object()
|
ALL = object()
|
||||||
|
|
||||||
def members_option(arg):
|
def members_option(arg):
|
||||||
|
"""Used to convert the :members: option to auto directives."""
|
||||||
if arg is None:
|
if arg is None:
|
||||||
return ALL
|
return ALL
|
||||||
return [x.strip() for x in arg.split(',')]
|
return [x.strip() for x in arg.split(',')]
|
||||||
|
|
||||||
def bool_option(arg):
|
def bool_option(arg):
|
||||||
|
"""Used to convert flag options to auto directives. (Instead of
|
||||||
|
directives.flag(), which returns None.)"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -191,12 +197,30 @@ def isdescriptor(x):
|
|||||||
|
|
||||||
|
|
||||||
class Documenter(object):
|
class Documenter(object):
|
||||||
# name by which the directive is called (auto...) and the default
|
"""
|
||||||
# generated directive name
|
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.
|
||||||
|
|
||||||
|
The *special_attrgetters* attribute is used to customize ``getattr()``
|
||||||
|
calls that the Documenter makes; its entries are of the form
|
||||||
|
``type: getattr_function``.
|
||||||
|
"""
|
||||||
|
#: name by which the directive is called (auto...) and the default
|
||||||
|
#: generated directive name
|
||||||
objtype = 'object'
|
objtype = 'object'
|
||||||
# indentation by which to indent the directive content
|
#: indentation by which to indent the directive content
|
||||||
content_indent = u' '
|
content_indent = u' '
|
||||||
# priority if multiple documenters return True from can_document_member
|
#: priority if multiple documenters return True from can_document_member
|
||||||
priority = 0
|
priority = 0
|
||||||
|
|
||||||
option_spec = {'noindex': bool_option}
|
option_spec = {'noindex': bool_option}
|
||||||
@@ -238,16 +262,26 @@ class Documenter(object):
|
|||||||
self.analyzer = None
|
self.analyzer = None
|
||||||
|
|
||||||
def add_line(self, line, source, *lineno):
|
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)
|
self.directive.result.append(self.indent + line, source, *lineno)
|
||||||
|
|
||||||
def resolve_name(self, modname, parents, path, base):
|
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')
|
raise NotImplementedError('must be implemented in subclasses')
|
||||||
|
|
||||||
def parse_name(self):
|
def parse_name(self):
|
||||||
"""
|
"""
|
||||||
Determine what module to import and what attribute to document.
|
Determine what module to import and what attribute to document.
|
||||||
|
|
||||||
Returns True if parsing and resolving was successful.
|
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
|
# first, parse the definition -- auto directives for classes and
|
||||||
# functions can contain a signature which is then used instead of
|
# functions can contain a signature which is then used instead of
|
||||||
@@ -276,11 +310,17 @@ class Documenter(object):
|
|||||||
|
|
||||||
self.args = args
|
self.args = args
|
||||||
self.retann = retann
|
self.retann = retann
|
||||||
self.fullname = (self.modname or '') + (self.objpath and
|
self.fullname = (self.modname or '') + \
|
||||||
'.' + '.'.join(self.objpath) or '')
|
(self.objpath and '.' + '.'.join(self.objpath) or '')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def import_object(self):
|
def import_object(self):
|
||||||
|
"""
|
||||||
|
Import the object given by *self.modname* and *self.objpath* and sets
|
||||||
|
it as *self.object*.
|
||||||
|
|
||||||
|
Returns True if successful, False if an error occurred.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
__import__(self.modname)
|
__import__(self.modname)
|
||||||
obj = self.module = sys.modules[self.modname]
|
obj = self.module = sys.modules[self.modname]
|
||||||
@@ -296,18 +336,34 @@ class Documenter(object):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_real_modname(self):
|
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
|
return self.get_attr(self.object, '__module__', None) or self.modname
|
||||||
|
|
||||||
def check_module(self):
|
def check_module(self):
|
||||||
|
"""
|
||||||
|
Check if *self.object* is really defined in the module given by
|
||||||
|
*self.modname*.
|
||||||
|
"""
|
||||||
modname = self.get_attr(self.object, '__module__', None)
|
modname = self.get_attr(self.object, '__module__', None)
|
||||||
if modname and modname != self.modname:
|
if modname and modname != self.modname:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def format_args(self):
|
def format_args(self):
|
||||||
|
"""
|
||||||
|
Format the argument signature of *self.object*. Should return None if
|
||||||
|
the object does not have a signature.
|
||||||
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def format_signature(self):
|
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:
|
if self.args is not None:
|
||||||
# signature given explicitly
|
# signature given explicitly
|
||||||
args = "(%s)" % self.args
|
args = "(%s)" % self.args
|
||||||
@@ -330,6 +386,7 @@ class Documenter(object):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
def add_directive_header(self, sig):
|
def add_directive_header(self, sig):
|
||||||
|
"""Add the directive header and options to the generated content."""
|
||||||
directive = getattr(self, 'directivetype', self.objtype)
|
directive = getattr(self, 'directivetype', self.objtype)
|
||||||
# the name to put into the generated directive -- doesn't contain
|
# the name to put into the generated directive -- doesn't contain
|
||||||
# the module (except for module directive of course)
|
# the module (except for module directive of course)
|
||||||
@@ -353,7 +410,7 @@ class Documenter(object):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def process_doc(self, docstrings):
|
def process_doc(self, docstrings):
|
||||||
"""Let the user process the docstrings."""
|
"""Let the user process the docstrings before adding them."""
|
||||||
for docstringlines in docstrings:
|
for docstringlines in docstrings:
|
||||||
if self.env.app:
|
if self.env.app:
|
||||||
# let extensions preprocess docstrings
|
# let extensions preprocess docstrings
|
||||||
@@ -364,6 +421,7 @@ class Documenter(object):
|
|||||||
yield line
|
yield line
|
||||||
|
|
||||||
def add_content(self, more_content, no_docstring=False):
|
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
|
# set sourcename and add content from attribute documentation
|
||||||
if self.analyzer:
|
if self.analyzer:
|
||||||
# prevent encoding errors when the file name is non-ASCII
|
# prevent encoding errors when the file name is non-ASCII
|
||||||
@@ -395,6 +453,12 @@ class Documenter(object):
|
|||||||
self.add_line(line, src[0], src[1])
|
self.add_line(line, src[0], src[1])
|
||||||
|
|
||||||
def get_object_members(self, want_all):
|
def get_object_members(self, want_all):
|
||||||
|
"""
|
||||||
|
Return (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).
|
||||||
|
"""
|
||||||
if not want_all:
|
if not want_all:
|
||||||
if not self.options.members:
|
if not self.options.members:
|
||||||
return False, []
|
return False, []
|
||||||
@@ -419,6 +483,15 @@ class Documenter(object):
|
|||||||
for mname in self.object.__dict__])
|
for mname in self.object.__dict__])
|
||||||
|
|
||||||
def filter_members(self, members, want_all):
|
def filter_members(self, members, want_all):
|
||||||
|
"""
|
||||||
|
Filter the given member list: members are skipped if
|
||||||
|
|
||||||
|
- they are private (except if given explicitly)
|
||||||
|
- they are undocumented (except if undoc-members is given)
|
||||||
|
|
||||||
|
The user can override the skipping decision by connecting to the
|
||||||
|
``autodoc-skip-member`` event.
|
||||||
|
"""
|
||||||
ret = []
|
ret = []
|
||||||
|
|
||||||
# search for members in source code too
|
# search for members in source code too
|
||||||
@@ -464,6 +537,10 @@ class Documenter(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def document_members(self, all_members=False):
|
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
|
# set current namespace for finding members
|
||||||
self.env.autodoc_current_module = self.modname
|
self.env.autodoc_current_module = self.modname
|
||||||
if self.objpath:
|
if self.objpath:
|
||||||
@@ -499,6 +576,14 @@ class Documenter(object):
|
|||||||
|
|
||||||
def generate(self, more_content=None, real_modname=None,
|
def generate(self, more_content=None, real_modname=None,
|
||||||
check_module=False, all_members=False):
|
check_module=False, all_members=False):
|
||||||
|
"""
|
||||||
|
Generate reST for the object given by *self.name*, and possibly 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():
|
if not self.parse_name():
|
||||||
# need a module to import
|
# need a module to import
|
||||||
self.directive.warn(
|
self.directive.warn(
|
||||||
@@ -563,6 +648,9 @@ class Documenter(object):
|
|||||||
|
|
||||||
|
|
||||||
class ModuleDocumenter(Documenter):
|
class ModuleDocumenter(Documenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for modules.
|
||||||
|
"""
|
||||||
objtype = 'module'
|
objtype = 'module'
|
||||||
content_indent = u''
|
content_indent = u''
|
||||||
|
|
||||||
@@ -625,6 +713,10 @@ class ModuleDocumenter(Documenter):
|
|||||||
|
|
||||||
|
|
||||||
class ModuleLevelDocumenter(Documenter):
|
class ModuleLevelDocumenter(Documenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for objects on module level (functions,
|
||||||
|
classes, data/constants).
|
||||||
|
"""
|
||||||
def resolve_name(self, modname, parents, path, base):
|
def resolve_name(self, modname, parents, path, base):
|
||||||
if modname is None:
|
if modname is None:
|
||||||
if path:
|
if path:
|
||||||
@@ -642,6 +734,10 @@ class ModuleLevelDocumenter(Documenter):
|
|||||||
|
|
||||||
|
|
||||||
class ClassLevelDocumenter(Documenter):
|
class ClassLevelDocumenter(Documenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for objects on class level (methods,
|
||||||
|
attributes).
|
||||||
|
"""
|
||||||
def resolve_name(self, modname, parents, path, base):
|
def resolve_name(self, modname, parents, path, base):
|
||||||
if modname is None:
|
if modname is None:
|
||||||
if path:
|
if path:
|
||||||
@@ -671,6 +767,9 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
|
|
||||||
|
|
||||||
class FunctionDocumenter(ModuleLevelDocumenter):
|
class FunctionDocumenter(ModuleLevelDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for functions.
|
||||||
|
"""
|
||||||
objtype = 'function'
|
objtype = 'function'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -701,6 +800,9 @@ class FunctionDocumenter(ModuleLevelDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class ClassDocumenter(ModuleLevelDocumenter):
|
class ClassDocumenter(ModuleLevelDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for classes.
|
||||||
|
"""
|
||||||
objtype = 'class'
|
objtype = 'class'
|
||||||
option_spec = {
|
option_spec = {
|
||||||
'members': members_option, 'undoc-members': bool_option,
|
'members': members_option, 'undoc-members': bool_option,
|
||||||
@@ -795,6 +897,9 @@ class ClassDocumenter(ModuleLevelDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class ExceptionDocumenter(ClassDocumenter):
|
class ExceptionDocumenter(ClassDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized ClassDocumenter subclass for exceptions.
|
||||||
|
"""
|
||||||
objtype = 'exception'
|
objtype = 'exception'
|
||||||
|
|
||||||
# needs a higher priority than ClassDocumenter
|
# needs a higher priority than ClassDocumenter
|
||||||
@@ -807,6 +912,9 @@ class ExceptionDocumenter(ClassDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class DataDocumenter(ModuleLevelDocumenter):
|
class DataDocumenter(ModuleLevelDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for data items.
|
||||||
|
"""
|
||||||
objtype = 'data'
|
objtype = 'data'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -818,6 +926,9 @@ class DataDocumenter(ModuleLevelDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class MethodDocumenter(ClassLevelDocumenter):
|
class MethodDocumenter(ClassLevelDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for methods (normal, static and class).
|
||||||
|
"""
|
||||||
objtype = 'method'
|
objtype = 'method'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -855,6 +966,9 @@ class MethodDocumenter(ClassLevelDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class AttributeDocumenter(ClassLevelDocumenter):
|
class AttributeDocumenter(ClassLevelDocumenter):
|
||||||
|
"""
|
||||||
|
Specialized Documenter subclass for attributes.
|
||||||
|
"""
|
||||||
objtype = 'attribute'
|
objtype = 'attribute'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -867,6 +981,11 @@ class AttributeDocumenter(ClassLevelDocumenter):
|
|||||||
|
|
||||||
|
|
||||||
class AutoDirective(Directive):
|
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.
|
||||||
|
"""
|
||||||
# a registry of objtype -> documenter class
|
# a registry of objtype -> documenter class
|
||||||
_registry = {}
|
_registry = {}
|
||||||
|
|
||||||
@@ -920,12 +1039,14 @@ class AutoDirective(Directive):
|
|||||||
|
|
||||||
|
|
||||||
def add_documenter(cls):
|
def add_documenter(cls):
|
||||||
|
"""Register a new Documenter."""
|
||||||
if not issubclass(cls, Documenter):
|
if not issubclass(cls, Documenter):
|
||||||
raise ExtensionError('autodoc documenter %r must be a subclass '
|
raise ExtensionError('autodoc documenter %r must be a subclass '
|
||||||
'of Documenter' % cls)
|
'of Documenter' % cls)
|
||||||
if cls.objtype in AutoDirective._registry:
|
# actually, it should be possible to override Documenters
|
||||||
raise ExtensionError('autodoc documenter for %r is already '
|
#if cls.objtype in AutoDirective._registry:
|
||||||
'registered' % cls.objtype)
|
# raise ExtensionError('autodoc documenter for %r is already '
|
||||||
|
# 'registered' % cls.objtype)
|
||||||
AutoDirective._registry[cls.objtype] = cls
|
AutoDirective._registry[cls.objtype] = cls
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user