Merge pull request #4366 from tk0miya/refactor_autodoc

Refactor autodoc
This commit is contained in:
Takeshi KOMIYA 2018-01-08 20:09:23 +09:00 committed by GitHub
commit 3965b1f023
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 267 additions and 169 deletions

View File

@ -24,6 +24,10 @@ Deprecated
``sphinx.util.inspect.Signature`` instead.
* ``sphinx.ext.autodoc.AutodocReporter`` is replaced by ``sphinx.util.docutils.
switch_source_input()`` and now deprecated. It will be removed in Sphinx-2.0.
* ``sphinx.ext.autodoc.add_documenter()`` and ``AutoDirective._register`` is now
deprecated. Please use ``app.add_autodocumenter()`` instead.
* ``AutoDirective._special_attrgetters`` is now deprecated. Please use
``app.add_autodoc_attrgetter()`` instead.
Features added
--------------

View File

@ -642,16 +642,14 @@ class Sphinx(object):
def add_autodocumenter(self, cls):
# type: (Any) -> None
logger.debug('[app] adding autodocumenter: %r', cls)
from sphinx.ext import autodoc
from sphinx.ext.autodoc.directive import AutodocDirective
autodoc.add_documenter(cls)
self.registry.add_documenter(cls.objtype, cls)
self.add_directive('auto' + cls.objtype, AutodocDirective)
def add_autodoc_attrgetter(self, type, getter):
# type: (Any, Callable) -> None
logger.debug('[app] adding autodoc attrgetter: %r', (type, getter))
from sphinx.ext import autodoc
autodoc.AutoDirective._special_attrgetters[type] = getter
def add_autodoc_attrgetter(self, typ, getter):
# type: (Type, Callable[[Any, unicode, Any], Any]) -> None
logger.debug('[app] adding autodoc attrgetter: %r', (typ, getter))
self.registry.add_autodoc_attrgetter(typ, getter)
def add_search_language(self, cls):
# type: (Any) -> None

View File

@ -14,16 +14,15 @@
import re
import sys
import inspect
import traceback
import warnings
from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
from six import iteritems, itervalues, text_type, class_types, string_types
from docutils.statemachine import ViewList
import sphinx
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.ext.autodoc.importer import mock, import_module
from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
@ -33,7 +32,7 @@ from sphinx.application import ExtensionError
from sphinx.util import logging
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
isenumclass, isenumattribute, getdoc
isenumattribute, getdoc
from sphinx.util.docstrings import prepare_docstring
if False:
@ -257,14 +256,10 @@ class Documenter(object):
option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable]
@staticmethod
def get_attr(obj, name, *defargs):
def get_attr(self, obj, name, *defargs):
# type: (Any, unicode, Any) -> Any
"""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)
return autodoc_attrgetter(self.env.app, obj, name, *defargs)
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
@ -297,6 +292,12 @@ class Documenter(object):
# the module analyzer to get at attribute docs, or None
self.analyzer = None # type: Any
@property
def documenters(self):
# type: () -> Dict[unicode, Type[Documenter]]
"""Returns registered Documenter classes"""
return get_documenters(self.env.app)
def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None
"""Append one line of generated reST to the output."""
@ -357,56 +358,15 @@ class Documenter(object):
Returns True if successful, False if an error occurred.
"""
if self.objpath:
logger.debug('[autodoc] from %s import %s',
self.modname, '.'.join(self.objpath))
# always enable mock import hook
# it will do nothing if autodoc_mock_imports is empty
with mock(self.env.config.autodoc_mock_imports):
try:
logger.debug('[autodoc] import %s', self.modname)
obj = import_module(self.modname, self.env.config.autodoc_warningiserror)
parent = None
self.module = obj
logger.debug('[autodoc] => %r', obj)
for part in self.objpath:
parent = obj
logger.debug('[autodoc] getattr(_, %r)', part)
obj = self.get_attr(obj, part)
logger.debug('[autodoc] => %r', obj)
self.object_name = part
self.parent = parent
self.object = obj
ret = import_object(self.modname, self.objpath, self.objtype,
attrgetter=self.get_attr,
warningiserror=self.env.config.autodoc_warningiserror)
self.module, self.parent, self.object_name, self.object = ret
return True
except (AttributeError, ImportError) as exc:
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(exc, ImportError):
# import_module() raises ImportError having real exception obj and
# traceback
real_exc, traceback_msg = exc.args
if isinstance(real_exc, SystemExit):
errmsg += ('; the module executes module level statement ' +
'and it might call sys.exit().')
elif isinstance(real_exc, ImportError):
errmsg += ('; the following exception was raised:\n%s' %
real_exc.args[0])
else:
errmsg += ('; the following exception was raised:\n%s' %
traceback_msg)
else:
errmsg += ('; the following exception was raised:\n%s' %
traceback.format_exc())
if PY2:
errmsg = errmsg.decode('utf-8') # type: ignore
logger.debug(errmsg)
self.directive.warn(errmsg)
except ImportError as exc:
self.directive.warn(exc.args[0])
self.env.note_reread()
return False
@ -579,57 +539,24 @@ class Documenter(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])
members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
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))
selected = []
for name in self.options.members:
if name in members:
selected.append((name, members[name].value))
else:
self.directive.warn('missing attribute %s in object %s' %
(name, self.fullname))
return False, sorted(selected)
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)
return False, sorted((m.name, m.value) for m in itervalues(members))
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))]
# Py34 doesn't have enum members in __dict__.
if isenumclass(self.object):
members.extend(
item for item in self.object.__members__.items()
if item not in members
)
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)
return False, sorted((m.name, m.value) for m in itervalues(members)
if m.directly_defined)
def filter_members(self, members, want_all):
# type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]]
@ -741,7 +668,7 @@ class Documenter(object):
# document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
for (mname, member, isattr) in self.filter_members(members, want_all):
classes = [cls for cls in itervalues(AutoDirective._registry)
classes = [cls for cls in itervalues(self.documenters)
if cls.can_document_member(member, mname, isattr, self)]
if not classes:
# don't know how to document this member
@ -1477,26 +1404,44 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
class DeprecatedDict(dict):
def __init__(self, message):
self.message = message
super(DeprecatedDict, self).__init__()
def __setitem__(self, key, value):
warnings.warn(self.message, RemovedInSphinx20Warning)
super(DeprecatedDict, self).__setitem__(key, value)
def setdefault(self, key, default=None):
warnings.warn(self.message, RemovedInSphinx20Warning)
super(DeprecatedDict, self).setdefault(key, default)
def update(self, other=None):
warnings.warn(self.message, RemovedInSphinx20Warning)
super(DeprecatedDict, self).update(other)
class AutodocRegistry(object):
"""
A registry of Documenters and attrgetters.
The *_registry* attribute is used to store registered Documenters.
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 = {} # type: Dict[unicode, Type[Documenter]]
# a registry of objtype -> documenter class (Deprecated)
_registry = DeprecatedDict(
'AutoDirective._registry has been deprecated. '
'Please use app.add_autodocumenter() instead.'
) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function
_special_attrgetters = {} # type: Dict[Type, Callable]
_special_attrgetters = DeprecatedDict(
'AutoDirective._special_attrgetters has been deprecated. '
'Please use app.add_autodoc_attrgetter() instead.'
) # type: Dict[Type, Callable]
AutoDirective = AutodocRegistry # for backward compatibility
@ -1505,6 +1450,10 @@ AutoDirective = AutodocRegistry # for backward compatibility
def add_documenter(cls):
# type: (Type[Documenter]) -> None
"""Register a new Documenter."""
warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. '
'Please use app.add_autodocumenter() instead.',
RemovedInSphinx20Warning)
if not issubclass(cls, Documenter):
raise ExtensionError('autodoc documenter %r must be a subclass '
'of Documenter' % cls)
@ -1515,6 +1464,29 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls
def get_documenters(app):
# type: (Sphinx) -> Dict[unicode, Type[Documenter]]
"""Returns registered Documenter classes"""
classes = dict(AutoDirective._registry) # registered directly
if app:
classes.update(app.registry.documenters) # registered by API
return classes
def autodoc_attrgetter(app, obj, name, *defargs):
# type: (Sphinx, Any, unicode, Any) -> Any
"""Alternative getattr() for types"""
candidates = dict(AutoDirective._special_attrgetters)
if app:
candidates.update(app.registry.autodoc_attrgettrs)
for typ, func in iteritems(candidates):
if isinstance(obj, typ):
return func(obj, name, *defargs)
return safe_getattr(obj, name, *defargs)
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)

View File

@ -12,7 +12,7 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils.utils import assemble_option_dict
from sphinx.ext.autodoc import AutoDirective
from sphinx.ext.autodoc import get_documenters
from sphinx.util import logging
from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
@ -127,7 +127,7 @@ class AutodocDirective(Directive):
# look up target Documenter
objtype = self.name[4:] # strip prefix (auto-).
doccls = AutoDirective._registry[objtype]
doccls = get_documenters(env.app)[objtype]
# process the options with the selected documenter's option_spec
try:

View File

@ -13,13 +13,17 @@ import sys
import warnings
import traceback
import contextlib
from collections import namedtuple
from types import FunctionType, MethodType, ModuleType
from six import PY2
from sphinx.util import logging
from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
from typing import Any, Generator, List, Set # NOQA
from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
logger = logging.getLogger(__name__)
@ -144,3 +148,86 @@ def import_module(modname, warningiserror=False):
# Importing modules may cause any side effects, including
# SystemExit, so we need to catch all errors.
raise ImportError(exc, traceback.format_exc())
def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
# type: (str, List[unicode], str, Callable[[Any, unicode], Any], bool) -> Any
if objpath:
logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
else:
logger.debug('[autodoc] import %s', modname)
try:
module = import_module(modname, warningiserror=warningiserror)
logger.debug('[autodoc] => %r', module)
obj = module
parent = None
object_name = None
for attrname in objpath:
parent = obj
logger.debug('[autodoc] getattr(_, %r)', attrname)
obj = attrgetter(obj, attrname)
logger.debug('[autodoc] => %r', obj)
object_name = attrname
return [module, parent, object_name, obj]
except (AttributeError, ImportError) as exc:
if objpath:
errmsg = ('autodoc: failed to import %s %r from module %r' %
(objtype, '.'.join(objpath), modname))
else:
errmsg = 'autodoc: failed to import %s %r' % (objtype, modname)
if isinstance(exc, ImportError):
# import_module() raises ImportError having real exception obj and
# traceback
real_exc, traceback_msg = exc.args
if isinstance(real_exc, SystemExit):
errmsg += ('; the module executes module level statement '
'and it might call sys.exit().')
elif isinstance(real_exc, ImportError):
errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
else:
errmsg += '; the following exception was raised:\n%s' % traceback_msg
else:
errmsg += '; the following exception was raised:\n%s' % traceback.format_exc()
if PY2:
errmsg = errmsg.decode('utf-8') # type: ignore
logger.debug(errmsg)
raise ImportError(errmsg)
Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
def get_object_members(subject, objpath, attrgetter, analyzer=None):
# type: (Any, List[unicode], Callable, Any) -> Dict[str, Attribute] # NOQA
"""Get members and attributes of target object."""
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
# Py34 doesn't have enum members in __dict__.
if sys.version_info[:2] == (3, 4) and isenumclass(subject):
obj_dict = dict(obj_dict)
for name, value in subject.__members__.items():
obj_dict[name] = value
members = {}
for name in dir(subject):
try:
value = attrgetter(subject, name)
directly_defined = name in obj_dict
members[name] = Attribute(name, directly_defined, value)
except AttributeError:
continue
if analyzer:
# append instance attributes (cf. self.attr1) if analyzer knows
from sphinx.ext.autodoc import INSTANCEATTR
namespace = '.'.join(objpath)
for (ns, name) in analyzer.find_attr_docs():
if namespace == ns and name not in members:
members[name] = Attribute(name, True, INSTANCEATTR)
return members

View File

@ -72,6 +72,7 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.ext.autodoc import get_documenters
from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
@ -158,8 +159,8 @@ class FakeDirective(DocumenterBridge):
super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore
def get_documenter(obj, parent):
# type: (Any, Any) -> Type[Documenter]
def get_documenter(app, obj, parent):
# type: (Sphinx, Any, Any) -> Type[Documenter]
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@ -167,8 +168,7 @@ def get_documenter(obj, parent):
another Python object (e.g. a module or a class) to which *obj*
belongs to.
"""
from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \
ModuleDocumenter
from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
if inspect.ismodule(obj):
# ModuleDocumenter.can_document_member always returns False
@ -176,7 +176,7 @@ def get_documenter(obj, parent):
# Construct a fake documenter for *parent*
if parent is not None:
parent_doc_cls = get_documenter(parent, None)
parent_doc_cls = get_documenter(app, parent, None)
else:
parent_doc_cls = ModuleDocumenter
@ -186,7 +186,7 @@ def get_documenter(obj, parent):
parent_doc = parent_doc_cls(FakeDirective(), "")
# Get the corrent documenter class for *obj*
classes = [cls for cls in AutoDirective._registry.values()
classes = [cls for cls in get_documenters(app).values()
if cls.can_document_member(obj, '', False, parent_doc)]
if classes:
classes.sort(key=lambda cls: cls.priority)
@ -289,7 +289,7 @@ class Autosummary(Directive):
full_name = modname + '::' + full_name[len(modname) + 1:]
# NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently
documenter = get_documenter(obj, parent)(self, full_name)
documenter = get_documenter(self.env.app, obj, parent)(self, full_name)
if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name)
items.append((display_name, '', '', real_name))
@ -615,7 +615,8 @@ def process_generate_options(app):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
suffix=suffix, base_path=app.srcdir)
suffix=suffix, base_path=app.srcdir,
app=app)
def setup(app):

View File

@ -33,24 +33,11 @@ from sphinx import __display_version__
from sphinx import package_dir
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.registry import SphinxComponentRegistry
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
# Add documenters to AutoDirective registry
from sphinx.ext.autodoc import add_documenter, \
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \
FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \
InstanceAttributeDocumenter
add_documenter(ModuleDocumenter)
add_documenter(ClassDocumenter)
add_documenter(ExceptionDocumenter)
add_documenter(DataDocumenter)
add_documenter(FunctionDocumenter)
add_documenter(MethodDocumenter)
add_documenter(AttributeDocumenter)
add_documenter(InstanceAttributeDocumenter)
if False:
# For type annotation
from typing import Any, Callable, Dict, Tuple, List # NOQA
@ -60,6 +47,30 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
class DummyApplication(object):
"""Dummy Application class for sphinx-autogen command."""
def __init__(self):
# type: () -> None
self.registry = SphinxComponentRegistry()
def setup_documenters(app):
# type: (Any) -> None
from sphinx.ext.autodoc import (
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter
)
documenters = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter
]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
def _simple_info(msg):
# type: (unicode) -> None
print(msg)
@ -81,8 +92,8 @@ def _underline(title, line='='):
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None,
imported_members=False):
# type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA
imported_members=False, app=None):
# type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
@ -148,7 +159,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn)
with open(fn, 'w') as f:
doc = get_documenter(obj, parent)
doc = get_documenter(app, obj, parent)
if template_name is not None:
template = template_env.get_template(template_name)
@ -167,7 +178,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
value = safe_getattr(obj, name)
except AttributeError:
continue
documenter = get_documenter(value, obj)
documenter = get_documenter(app, value, obj)
if documenter.objtype == typ:
if typ == 'method':
items.append(name)
@ -392,11 +403,14 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]):
# type: (List[str]) -> None
app = DummyApplication()
setup_documenters(app)
args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,
template_dir=args.templates,
imported_members=args.imported_members)
imported_members=args.imported_members,
app=app)
if __name__ == '__main__':

View File

@ -38,6 +38,7 @@ if False:
from sphinx.builders import Builder # NOQA
from sphinx.domains import Domain, Index # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc import Documenter # NOQA
from sphinx.util.typing import RoleFunction # NOQA
logger = logging.getLogger(__name__)
@ -51,7 +52,9 @@ EXTENSION_BLACKLIST = {
class SphinxComponentRegistry(object):
def __init__(self):
self.autodoc_attrgettrs = {} # type: Dict[Type, Callable[[Any, unicode, Any], Any]]
self.builders = {} # type: Dict[unicode, Type[Builder]]
self.documenters = {} # type: Dict[unicode, Type[Documenter]]
self.domains = {} # type: Dict[unicode, Type[Domain]]
self.domain_directives = {} # type: Dict[unicode, Dict[unicode, Any]]
self.domain_indices = {} # type: Dict[unicode, List[Type[Index]]]
@ -284,6 +287,14 @@ class SphinxComponentRegistry(object):
# type: () -> List[Type[Transform]]
return self.post_transforms
def add_documenter(self, objtype, documenter):
# type: (unicode, Type[Documenter]) -> None
self.documenters[objtype] = documenter
def add_autodoc_attrgetter(self, typ, attrgetter):
# type: (Type, Callable[[Any, unicode, Any], Any]) -> None
self.autodoc_attrgettrs[typ] = attrgetter
def load_extension(self, app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""

View File

@ -112,7 +112,7 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert len(_warnings) == 1, _warnings
@ -120,7 +120,7 @@ def test_generate():
del _warnings[:]
def assert_works(objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
@ -134,7 +134,7 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@ -142,7 +142,7 @@ def test_generate():
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert len(_warnings) == 0, _warnings

View File

@ -121,7 +121,7 @@ def skip_member(app, what, name, obj, skip, options):
@pytest.mark.usefixtures('setup_test')
def test_parse_name():
def verify(objtype, name, result):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
assert inst.parse_name()
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
@ -164,7 +164,7 @@ def test_parse_name():
@pytest.mark.usefixtures('setup_test')
def test_format_signature():
def formatsig(objtype, name, obj, args, retann):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.fullname = name
inst.doc_as_attr = False # for class objtype
inst.object = obj
@ -270,7 +270,7 @@ def test_format_signature():
@pytest.mark.usefixtures('setup_test')
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
inst = AutoDirective._registry[objtype](directive, 'tmp')
inst = app.registry.documenters[objtype](directive, 'tmp')
inst.object = obj
inst.objpath = [obj.__name__]
inst.doc_as_attr = False
@ -449,7 +449,7 @@ def test_get_doc():
@pytest.mark.usefixtures('setup_test')
def test_docstring_processing():
def process(objtype, name, obj):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.object = obj
inst.fullname = name
return list(inst.process_doc(inst.get_doc()))
@ -506,7 +506,7 @@ def test_docstring_property_processing():
def genarate_docstring(objtype, name, **kw):
del processed_docstrings[:]
del processed_signatures[:]
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
results = list(directive.result)
docstrings = inst.get_doc()[0]
@ -555,7 +555,7 @@ def test_new_documenter():
add_documenter(MyDocumenter)
def assert_result_contains(item, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@ -581,7 +581,7 @@ def test_attrgetter_using():
AutoDirective._special_attrgetters[type] = special_getattr
del getattr_spy[:]
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
hooked_members = [s[1] for s in getattr_spy]
@ -603,7 +603,7 @@ def test_attrgetter_using():
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert len(_warnings) == 1, _warnings
@ -611,7 +611,7 @@ def test_generate():
del _warnings[:]
def assert_works(objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
@ -625,7 +625,7 @@ def test_generate():
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
@ -633,7 +633,7 @@ def test_generate():
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert len(_warnings) == 0, _warnings

View File

@ -57,10 +57,14 @@ def test_mangle_signature():
@pytest.mark.sphinx('dummy', **default_kw)
def test_get_items_summary(app, status, warning):
def test_get_items_summary(make_app, app_params):
import sphinx.ext.autosummary
import sphinx.ext.autosummary.generate
args, kwargs = app_params
app = make_app(*args, **kwargs)
sphinx.ext.autosummary.generate.setup_documenters(app)
# monkey-patch Autosummary.get_items so we can easily get access to it's
# results..
import sphinx.ext.autosummary
orig_get_items = sphinx.ext.autosummary.Autosummary.get_items
autosummary_items = {}
@ -85,7 +89,7 @@ def test_get_items_summary(app, status, warning):
finally:
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
html_warnings = warning.getvalue()
html_warnings = app._warning.getvalue()
assert html_warnings == ''
expected_values = {

View File

@ -10,10 +10,14 @@
"""
import pytest
from sphinx.ext.autosummary.generate import setup_documenters
@pytest.mark.sphinx('html', testroot='templating')
def test_layout_overloading(app, status, warning):
def test_layout_overloading(make_app, app_params):
args, kwargs = app_params
app = make_app(*args, **kwargs)
setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'contents.html').text(encoding='utf-8')
@ -22,7 +26,10 @@ def test_layout_overloading(app, status, warning):
@pytest.mark.sphinx('html', testroot='templating')
def test_autosummary_class_template_overloading(app, status, warning):
def test_autosummary_class_template_overloading(make_app, app_params):
args, kwargs = app_params
app = make_app(*args, **kwargs)
setup_documenters(app)
app.builder.build_update()
result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(