Merge pull request #4299 from tk0miya/refactor_MockImporter

autodoc: refactor MockImporter
This commit is contained in:
Takeshi KOMIYA 2017-12-14 22:13:16 +09:00 committed by GitHub
commit 9d44cb5952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 44 deletions

View File

@ -24,7 +24,8 @@ from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
from sphinx.ext.autodoc.importer import _MockImporter, import_module
from sphinx.ext.autodoc.importer import mock, import_module
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
from sphinx.locale import _
@ -388,47 +389,45 @@ class Documenter(object):
self.modname, '.'.join(self.objpath))
# always enable mock import hook
# it will do nothing if autodoc_mock_imports is empty
import_hook = _MockImporter(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)
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)
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().')
elif isinstance(e, ImportError):
errmsg += '; the following exception was raised:\n%s' % e.args[0]
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)
self.env.note_reread()
return False
finally:
import_hook.disable()
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
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().')
elif isinstance(e, ImportError):
errmsg += '; the following exception was raised:\n%s' % e.args[0]
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)
self.env.note_reread()
return False
def get_real_modname(self):
# type: () -> str

View File

@ -10,15 +10,16 @@
"""
import sys
import traceback
import warnings
import traceback
import contextlib
from types import FunctionType, MethodType, ModuleType
from sphinx.util import logging
if False:
# For type annotation
from typing import Any, List, Set # NOQA
from typing import Any, Generator, List, Set # NOQA
logger = logging.getLogger(__name__)
@ -77,7 +78,6 @@ class _MockModule(ModuleType):
class _MockImporter(object):
def __init__(self, names):
# type: (List[str]) -> None
self.base_packages = set() # type: Set[str]
@ -120,6 +120,16 @@ class _MockImporter(object):
return module
@contextlib.contextmanager
def mock(names):
# type: (List[str]) -> Generator
try:
importer = _MockImporter(names)
yield
finally:
importer.disable()
def import_module(modname, warningiserror=False):
"""
Call __import__(modname), convert exceptions to ImportError