Migrate to py3 style type annotation: sphinx.ext.autodoc.mock

This commit is contained in:
Takeshi KOMIYA 2019-06-30 15:02:35 +09:00
parent d057c5e6d7
commit e67f5db296

View File

@ -15,14 +15,11 @@ import warnings
from importlib.abc import Loader, MetaPathFinder from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec from importlib.machinery import ModuleSpec
from types import FunctionType, MethodType, ModuleType from types import FunctionType, MethodType, ModuleType
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union
from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.util import logging from sphinx.util import logging
if False:
# For type annotation
from typing import Any, Generator, Iterator, List, Sequence, Tuple, Union # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -31,8 +28,7 @@ class _MockObject:
__display_name__ = '_MockObject' __display_name__ = '_MockObject'
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs) -> Any:
# type: (Any, Any) -> Any
if len(args) == 3 and isinstance(args[1], tuple): if len(args) == 3 and isinstance(args[1], tuple):
superclass = args[1][-1].__class__ superclass = args[1][-1].__class__
if superclass is cls: if superclass is cls:
@ -42,48 +38,39 @@ class _MockObject:
return super().__new__(cls) return super().__new__(cls)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs) -> None:
# type: (Any, Any) -> None
self.__qualname__ = '' self.__qualname__ = ''
def __len__(self): def __len__(self) -> int:
# type: () -> int
return 0 return 0
def __contains__(self, key): def __contains__(self, key: str) -> bool:
# type: (str) -> bool
return False return False
def __iter__(self): def __iter__(self) -> Iterator:
# type: () -> Iterator
return iter([]) return iter([])
def __mro_entries__(self, bases): def __mro_entries__(self, bases: Tuple) -> Tuple:
# type: (Tuple) -> Tuple
return (self.__class__,) return (self.__class__,)
def __getitem__(self, key): def __getitem__(self, key: str) -> "_MockObject":
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)() return _make_subclass(key, self.__display_name__, self.__class__)()
def __getattr__(self, key): def __getattr__(self, key: str) -> "_MockObject":
# type: (str) -> _MockObject
return _make_subclass(key, self.__display_name__, self.__class__)() return _make_subclass(key, self.__display_name__, self.__class__)()
def __call__(self, *args, **kw): def __call__(self, *args, **kw) -> Any:
# type: (Any, Any) -> Any
if args and type(args[0]) in [FunctionType, MethodType]: if args and type(args[0]) in [FunctionType, MethodType]:
# Appears to be a decorator, pass through unchanged # Appears to be a decorator, pass through unchanged
return args[0] return args[0]
return self return self
def __repr__(self): def __repr__(self) -> str:
# type: () -> str
return self.__display_name__ return self.__display_name__
def _make_subclass(name, module, superclass=_MockObject, attributes=None): def _make_subclass(name: str, module: str, superclass: Any = _MockObject,
# type: (str, str, Any, dict) -> Any attributes: Any = None) -> Any:
attrs = {'__module__': module, '__display_name__': module + '.' + name} attrs = {'__module__': module, '__display_name__': module + '.' + name}
attrs.update(attributes or {}) attrs.update(attributes or {})
@ -94,8 +81,7 @@ class _MockModule(ModuleType):
"""Used by autodoc_mock_imports.""" """Used by autodoc_mock_imports."""
__file__ = os.devnull __file__ = os.devnull
def __init__(self, name, loader=None): def __init__(self, name: str, loader: "_MockImporter" = None) -> None:
# type: (str, _MockImporter) -> None
super().__init__(name) super().__init__(name)
self.__all__ = [] # type: List[str] self.__all__ = [] # type: List[str]
self.__path__ = [] # type: List[str] self.__path__ = [] # type: List[str]
@ -104,18 +90,15 @@ class _MockModule(ModuleType):
warnings.warn('The loader argument for _MockModule is deprecated.', warnings.warn('The loader argument for _MockModule is deprecated.',
RemovedInSphinx30Warning) RemovedInSphinx30Warning)
def __getattr__(self, name): def __getattr__(self, name: str) -> _MockObject:
# type: (str) -> _MockObject
return _make_subclass(name, self.__name__)() return _make_subclass(name, self.__name__)()
def __repr__(self): def __repr__(self) -> str:
# type: () -> str
return self.__name__ return self.__name__
class _MockImporter(MetaPathFinder): class _MockImporter(MetaPathFinder):
def __init__(self, names): def __init__(self, names: List[str]) -> None:
# type: (List[str]) -> None
self.names = names self.names = names
self.mocked_modules = [] # type: List[str] self.mocked_modules = [] # type: List[str]
# enable hook by adding itself to meta_path # enable hook by adding itself to meta_path
@ -124,8 +107,7 @@ class _MockImporter(MetaPathFinder):
warnings.warn('_MockImporter is now deprecated.', warnings.warn('_MockImporter is now deprecated.',
RemovedInSphinx30Warning) RemovedInSphinx30Warning)
def disable(self): def disable(self) -> None:
# type: () -> None
# remove `self` from `sys.meta_path` to disable import hook # remove `self` from `sys.meta_path` to disable import hook
sys.meta_path = [i for i in sys.meta_path if i is not self] sys.meta_path = [i for i in sys.meta_path if i is not self]
# remove mocked modules from sys.modules to avoid side effects after # remove mocked modules from sys.modules to avoid side effects after
@ -134,16 +116,14 @@ class _MockImporter(MetaPathFinder):
if m in sys.modules: if m in sys.modules:
del sys.modules[m] del sys.modules[m]
def find_module(self, name, path=None): def find_module(self, name: str, path: Sequence[Union[bytes, str]] = None) -> Any:
# type: (str, Sequence[Union[bytes, str]]) -> Any
# check if name is (or is a descendant of) one of our base_packages # check if name is (or is a descendant of) one of our base_packages
for n in self.names: for n in self.names:
if n == name or name.startswith(n + '.'): if n == name or name.startswith(n + '.'):
return self return self
return None return None
def load_module(self, name): def load_module(self, name: str) -> ModuleType:
# type: (str) -> ModuleType
if name in sys.modules: if name in sys.modules:
# module has already been imported, return it # module has already been imported, return it
return sys.modules[name] return sys.modules[name]
@ -157,34 +137,30 @@ class _MockImporter(MetaPathFinder):
class MockLoader(Loader): class MockLoader(Loader):
"""A loader for mocking.""" """A loader for mocking."""
def __init__(self, finder): def __init__(self, finder: "MockFinder") -> None:
# type: (MockFinder) -> None
super().__init__() super().__init__()
self.finder = finder self.finder = finder
def create_module(self, spec): def create_module(self, spec: ModuleSpec) -> ModuleType:
# type: (ModuleSpec) -> ModuleType
logger.debug('[autodoc] adding a mock module as %s!', spec.name) logger.debug('[autodoc] adding a mock module as %s!', spec.name)
self.finder.mocked_modules.append(spec.name) self.finder.mocked_modules.append(spec.name)
return _MockModule(spec.name) return _MockModule(spec.name)
def exec_module(self, module): def exec_module(self, module: ModuleType) -> None:
# type: (ModuleType) -> None
pass # nothing to do pass # nothing to do
class MockFinder(MetaPathFinder): class MockFinder(MetaPathFinder):
"""A finder for mocking.""" """A finder for mocking."""
def __init__(self, modnames): def __init__(self, modnames: List[str]) -> None:
# type: (List[str]) -> None
super().__init__() super().__init__()
self.modnames = modnames self.modnames = modnames
self.loader = MockLoader(self) self.loader = MockLoader(self)
self.mocked_modules = [] # type: List[str] self.mocked_modules = [] # type: List[str]
def find_spec(self, fullname, path, target=None): def find_spec(self, fullname: str, path: Sequence[Union[bytes, str]],
# type: (str, Sequence[Union[bytes, str]], ModuleType) -> ModuleSpec target: ModuleType = None) -> ModuleSpec:
for modname in self.modnames: for modname in self.modnames:
# check if fullname is (or is a descendant of) one of our targets # check if fullname is (or is a descendant of) one of our targets
if modname == fullname or fullname.startswith(modname + '.'): if modname == fullname or fullname.startswith(modname + '.'):
@ -192,16 +168,14 @@ class MockFinder(MetaPathFinder):
return None return None
def invalidate_caches(self): def invalidate_caches(self) -> None:
# type: () -> None
"""Invalidate mocked modules on sys.modules.""" """Invalidate mocked modules on sys.modules."""
for modname in self.mocked_modules: for modname in self.mocked_modules:
sys.modules.pop(modname, None) sys.modules.pop(modname, None)
@contextlib.contextmanager @contextlib.contextmanager
def mock(modnames): def mock(modnames: List[str]) -> Generator[None, None, None]:
# type: (List[str]) -> Generator[None, None, None]
"""Insert mock modules during context:: """Insert mock modules during context::
with mock(['target.module.name']): with mock(['target.module.name']):