You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/autodocsumm/__init__.py

588 lines
22 KiB

"""Sphinx extension that defines new auto documenters with autosummary.
The :class:`AutoSummModuleDocumenter` and :class:`AutoSummClassDocumenter`
classes defined here enable an autosummary-style listing of the corresponding
members.
This extension gives also the possibility to choose which data shall be shown
and to include the docstring of the ``'__call__'`` attribute.
"""
from itertools import chain
from sphinx.util import logging
import re
from docutils import nodes
import sphinx
from sphinx.util.docutils import SphinxDirective
from sphinx.ext.autodoc import (
ClassDocumenter, ModuleDocumenter, ALL, PycodeError,
ModuleAnalyzer, bool_option, AttributeDocumenter, DataDocumenter, Options,
prepare_docstring)
import sphinx.ext.autodoc as ad
signature = Signature = None
from sphinx.ext.autodoc.directive import (
AutodocDirective, AUTODOC_DEFAULT_OPTIONS, process_documenter_options,
DocumenterBridge
)
from sphinx.ext.autodoc import get_documenters
try:
from sphinx.util.inspect import signature, stringify_signature
except ImportError:
from sphinx.ext.autodoc import Signature
sphinx_version = list(map(float, re.findall(r'\d+', sphinx.__version__)[:3]))
from sphinx.util import force_decode
try:
from cyordereddict import OrderedDict
except ImportError:
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
__version__ = '0.2.2'
__author__ = "Philipp S. Sommer"
logger = logging.getLogger(__name__)
#: Options of the :class:`sphinx.ext.autodoc.ModuleDocumenter` that have an
#: effect on the selection of members for the documentation
member_options = {
'members', 'undoc-members', 'inherited-members', 'exclude-members',
'private-members', 'special-members', 'imported-members',
'ignore-module-all'}
if signature is not None: # sphinx >= 2.4.0
def process_signature(obj):
sig = signature(obj)
return stringify_signature(sig)
elif Signature is not None: # sphinx >= 1.7
def process_signature(obj):
try:
args = Signature(obj).format_args()
except TypeError:
return None
else:
args = args.replace('\\', '\\\\')
return args
def list_option(option):
"""Transform a string to a list by splitting at ;;."""
return [s.strip() for s in option.split(";;")]
class AutosummaryDocumenter(object):
"""Abstract class for for extending Documenter methods
This classed is used as a base class for Documenters in order to provide
the necessary methods for generating the autosummary."""
#: List of functions that may filter the members
filter_funcs = []
#: Grouper functions
grouper_funcs = []
def __init__(self):
raise NotImplementedError
def get_grouped_documenters(self, all_members=False):
"""Method to return the member documenters
This method is somewhat like a combination of the
:meth:`sphinx.ext.autodoc.ModuleDocumenter.generate` method and the
:meth:`sphinx.ext.autodoc.ModuleDocumenter.document_members` method.
Hence it initializes this instance by importing the object, etc. and
it finds the documenters to use for the autosummary option in the same
style as the document_members does it.
Returns
-------
dict
dictionary whose keys are determined by the :attr:`member_sections`
dictionary and whose values are lists of tuples. Each tuple
consists of a documenter and a boolean to identify whether a module
check should be made describes an attribute or not.
Notes
-----
If a :class:`sphinx.ext.autodoc.Documenter.member_order` value is not
in the :attr:`member_sections` dictionary, it will be put into an
additional `Miscellaneous` section."""
use_sections = self.options.autosummary_sections
self.parse_name()
self.import_object()
# 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 = None 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:
logger.debug('[autodocsumm] 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)
self.env.temp_data['autodoc:module'] = self.modname
if self.objpath:
self.env.temp_data['autodoc:class'] = self.objpath[0]
if not self.options.autosummary_force_inline:
docstring = self.get_doc()
autodocsumm_directive = '.. auto%ssumm::' % self.objtype
for s in chain.from_iterable(docstring):
if autodocsumm_directive in s:
return {}
# set the members from the autosummary member options
options_save = {}
for option in member_options.intersection(self.option_spec):
autopt = 'autosummary-' + option
if getattr(self.options, autopt):
options_save[option] = getattr(self.options, option)
self.options[option] = getattr(self.options, autopt)
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 = []
registry = get_documenters(self.env.app)
for (mname, member, isattr) in self.filter_members(members, want_all):
classes = [cls for cls in registry.values()
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,
members_check_module and not isattr))
member_order = (
self.options.member_order or self.env.config.autodoc_member_order
)
try:
memberdocumenters = self.sort_members(
memberdocumenters, member_order
)
except AttributeError: # sphinx<3.0
pass
documenters = OrderedDict()
for e in memberdocumenters:
section = self.member_sections.get(
e[0].member_order, 'Miscellaneous')
if self.env.app:
e[0].parse_name()
e[0].import_object()
if members_check_module and not e[0].check_module():
continue
user_section = self.env.app.emit_firstresult(
'autodocsumm-grouper', self.objtype, e[0].object_name,
e[0].object, section, self.object)
section = user_section or section
if not use_sections or section in use_sections:
documenters.setdefault(section, []).append(e)
self.options.update(options_save)
return documenters
def add_autosummary(self):
"""Add the autosammary table of this documenter."""
if self.options.autosummary:
grouped_documenters = self.get_grouped_documenters()
sourcename = self.get_sourcename()
for section, documenters in grouped_documenters.items():
if not self.options.autosummary_no_titles:
self.add_line('**%s:**' % section, sourcename)
self.add_line('', sourcename)
self.add_line('.. autosummary::', sourcename)
self.add_line('', sourcename)
indent = ' '
for (documenter, _) in documenters:
self.add_line(
indent + '~' + documenter.fullname, sourcename)
self.add_line('', sourcename)
class AutoSummModuleDocumenter(ModuleDocumenter, AutosummaryDocumenter):
"""Module documentor with autosummary tables of its members.
This class has the same functionality as the base
:class:`sphinx.ext.autodoc.ModuleDocumenter` class but with an additional
`autosummary`.
It's priority is slightly higher than the one of the ModuleDocumenter.
"""
#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.ModuleDocumenter`
priority = ModuleDocumenter.priority + 0.1
#: original option_spec from :class:`sphinx.ext.autodoc.ModuleDocumenter`
#: but with additional autosummary boolean option
option_spec = ModuleDocumenter.option_spec.copy()
option_spec['autosummary'] = bool_option
option_spec['autosummary-no-nesting'] = bool_option
option_spec['autosummary-sections'] = list_option
option_spec['autosummary-no-titles'] = bool_option
option_spec['autosummary-force-inline'] = bool_option
#: Add options for members for the autosummary
for _option in member_options.intersection(option_spec):
option_spec['autosummary-' + _option] = option_spec[_option]
del _option
member_sections = OrderedDict([
(ad.ClassDocumenter.member_order, 'Classes'),
(ad.ExceptionDocumenter.member_order, 'Exceptions'),
(ad.FunctionDocumenter.member_order, 'Functions'),
(ad.DataDocumenter.member_order, 'Data'),
])
""":class:`~collections.OrderedDict` that includes the autosummary sections
This dictionary defines the sections for the autosummmary option. The
values correspond to the :attr:`sphinx.ext.autodoc.Documenter.member_order`
attribute that shall be used for each section."""
def add_content(self, *args, **kwargs):
super().add_content(*args, **kwargs)
self.add_autosummary()
if self.options.autosummary_no_nesting:
self.options.autosummary = False
class AutoSummClassDocumenter(ClassDocumenter, AutosummaryDocumenter):
"""Class documentor with autosummary tables for its members.
This class has the same functionality as the base
:class:`sphinx.ext.autodoc.ClassDocumenter` class but with an additional
`autosummary` option to provide the ability to provide a summary of all
methods and attributes.
It's priority is slightly higher than the one of the ClassDocumenter
"""
#: slightly higher priority than
#: :class:`sphinx.ext.autodoc.ClassDocumenter`
priority = ClassDocumenter.priority + 0.1
#: original option_spec from :class:`sphinx.ext.autodoc.ClassDocumenter`
#: but with additional autosummary boolean option
option_spec = ClassDocumenter.option_spec.copy()
option_spec['autosummary'] = bool_option
option_spec['autosummary-no-nesting'] = bool_option
option_spec['autosummary-sections'] = list_option
option_spec['autosummary-no-titles'] = bool_option
option_spec['autosummary-force-inline'] = bool_option
#: Add options for members for the autosummary
for _option in member_options.intersection(option_spec):
option_spec['autosummary-' + _option] = option_spec[_option]
del _option
member_sections = OrderedDict([
(ad.ClassDocumenter.member_order, 'Classes'),
(ad.MethodDocumenter.member_order, 'Methods'),
(ad.AttributeDocumenter.member_order, 'Attributes'),
])
""":class:`~collections.OrderedDict` that includes the autosummary sections
This dictionary defines the sections for the autosummmary option. The
values correspond to the :attr:`sphinx.ext.autodoc.Documenter.member_order`
attribute that shall be used for each section."""
def add_content(self, *args, **kwargs):
super().add_content(*args, **kwargs)
self.add_autosummary()
class CallableDataDocumenter(DataDocumenter):
""":class:`sphinx.ext.autodoc.DataDocumenter` that uses the __call__ attr
"""
priority = DataDocumenter.priority + 0.1
def format_args(self):
# for classes, the relevant signature is the __init__ method's
callmeth = self.get_attr(self.object, '__call__', None)
if callmeth is None:
return None
return process_signature(callmeth)
def get_doc(self, encoding=None, ignore=1):
"""Reimplemented to include data from the call method"""
content = self.env.config.autodata_content
if content not in ('both', 'call') or not self.get_attr(
self.get_attr(self.object, '__call__', None), '__doc__'):
return super(CallableDataDocumenter, self).get_doc(
encoding=encoding, ignore=ignore)
# for classes, what the "docstring" is can be controlled via a
# config value; the default is both docstrings
docstrings = []
if content != 'call':
docstring = self.get_attr(self.object, '__doc__', None)
docstrings = [docstring + '\n'] if docstring else []
calldocstring = self.get_attr(
self.get_attr(self.object, '__call__', None), '__doc__')
if docstrings:
docstrings[0] += calldocstring
else:
docstrings.append(calldocstring + '\n')
doc = []
for docstring in docstrings:
if not isinstance(docstring, str):
docstring = force_decode(docstring, encoding)
doc.append(prepare_docstring(docstring, ignore))
return doc
class CallableAttributeDocumenter(AttributeDocumenter):
""":class:`sphinx.ext.autodoc.AttributeDocumenter` that uses the __call__
attr
"""
priority = AttributeDocumenter.priority + 0.1
def format_args(self):
# for classes, the relevant signature is the __init__ method's
callmeth = self.get_attr(self.object, '__call__', None)
if callmeth is None:
return None
return process_signature(callmeth)
def get_doc(self, encoding=None, ignore=1):
"""Reimplemented to include data from the call method"""
content = self.env.config.autodata_content
if content not in ('both', 'call') or not self.get_attr(
self.get_attr(self.object, '__call__', None), '__doc__'):
return super(CallableAttributeDocumenter, self).get_doc(
encoding=encoding, ignore=ignore)
# for classes, what the "docstring" is can be controlled via a
# config value; the default is both docstrings
docstrings = []
if content != 'call':
docstring = self.get_attr(self.object, '__doc__', None)
docstrings = [docstring + '\n'] if docstring else []
calldocstring = self.get_attr(
self.get_attr(self.object, '__call__', None), '__doc__')
if docstrings:
docstrings[0] += calldocstring
else:
docstrings.append(calldocstring + '\n')
doc = []
for docstring in docstrings:
if not isinstance(docstring, str):
docstring = force_decode(docstring, encoding)
doc.append(prepare_docstring(docstring, ignore))
return doc
def dont_document_data(config, fullname):
"""Check whether the given object should be documented
Parameters
----------
config: sphinx.Options
The configuration
fullname: str
The name of the object
Returns
-------
bool
Whether the data of `fullname` should be excluded or not"""
if config.document_data is True:
document_data = [re.compile('.*')]
else:
document_data = config.document_data
if config.not_document_data is True:
not_document_data = [re.compile('.*')]
else:
not_document_data = config.not_document_data
return (
# data should not be documented
(any(re.match(p, fullname) for p in not_document_data)) or
# or data is not included in what should be documented
(not any(re.match(p, fullname) for p in document_data)))
class NoDataDataDocumenter(CallableDataDocumenter):
"""DataDocumenter that prevents the displaying of large data"""
#: slightly higher priority as the one of the CallableDataDocumenter
priority = CallableDataDocumenter.priority + 0.1
def __init__(self, *args, **kwargs):
super(NoDataDataDocumenter, self).__init__(*args, **kwargs)
fullname = '.'.join(self.name.rsplit('::', 1))
if hasattr(self.env, 'config') and dont_document_data(
self.env.config, fullname):
self.options = Options(self.options)
self.options.annotation = ' '
class NoDataAttributeDocumenter(CallableAttributeDocumenter):
"""AttributeDocumenter that prevents the displaying of large data"""
#: slightly higher priority as the one of the CallableAttributeDocumenter
priority = CallableAttributeDocumenter.priority + 0.1
def __init__(self, *args, **kwargs):
super(NoDataAttributeDocumenter, self).__init__(*args, **kwargs)
fullname = '.'.join(self.name.rsplit('::', 1))
if hasattr(self.env, 'config') and dont_document_data(
self.env.config, fullname):
self.options = Options(self.options)
self.options.annotation = ' '
class AutoDocSummDirective(SphinxDirective):
"""A directive to generate an autosummary.
Usage::
.. autoclasssum:: <Class>
.. automodsum:: <module>
The directive additionally supports all options of the ``autoclass`` or
``automod`` directive respectively. Sections can be a list of section titles
to be included. If ommitted, all sections are used.
"""
has_content = False
option_spec = AutodocDirective.option_spec
required_arguments = 1
optional_arguments = 0
def run(self):
reporter = self.state.document.reporter
try:
source, lineno = reporter.get_source_and_line(self.lineno)
except AttributeError:
source, lineno = (None, None)
# look up target Documenter
objtype = self.name[4:-4] # strip prefix (auto-) and suffix (-summ).
doccls = self.env.app.registry.documenters[objtype]
self.options['autosummary-force-inline'] = True
self.options['autosummary'] = True
if 'no-members' not in self.options:
self.options['members'] = True
# process the options with the selected documenter's option_spec
try:
documenter_options = process_documenter_options(doccls, self.config,
self.options)
except (KeyError, ValueError, TypeError) as exc:
# an option is either unknown or has a wrong type
logger.error(
'An option to %s is either unknown or has an invalid '
'value: %s', self.name, exc,
location=(self.env.docname, lineno))
return []
# generate the output
params = DocumenterBridge(self.env, reporter, documenter_options,
lineno, self.state)
documenter = doccls(params, self.arguments[0])
documenter.add_autosummary()
node = nodes.paragraph()
node.document = self.state.document
self.state.nested_parse(params.result, 0, node)
return node.children
def setup(app):
"""setup function for using this module as a sphinx extension"""
app.setup_extension('sphinx.ext.autosummary')
app.setup_extension('sphinx.ext.autodoc')
app.add_directive('autoclasssumm', AutoDocSummDirective)
app.add_directive('automodulesumm', AutoDocSummDirective)
AUTODOC_DEFAULT_OPTIONS.extend(
[option for option in AutoSummModuleDocumenter.option_spec
if option not in AUTODOC_DEFAULT_OPTIONS])
AUTODOC_DEFAULT_OPTIONS.extend(
[option for option in AutoSummClassDocumenter.option_spec
if option not in AUTODOC_DEFAULT_OPTIONS])
# make sure to allow inheritance when registering new documenters
registry = get_documenters(app)
for cls in [AutoSummClassDocumenter, AutoSummModuleDocumenter,
CallableAttributeDocumenter, NoDataDataDocumenter,
NoDataAttributeDocumenter]:
if not issubclass(registry.get(cls.objtype), cls):
app.add_autodocumenter(cls, override=True)
# group event
app.add_event('autodocsumm-grouper')
# config value
app.add_config_value('autodata_content', 'class', True)
app.add_config_value('document_data', True, True)
app.add_config_value('not_document_data', [], True)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}