mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
refactor `mock()
` to based on PEP-451 implementation
This commit is contained in:
parent
ab3eb1b61d
commit
1a50d34520
2
CHANGES
2
CHANGES
@ -39,12 +39,14 @@ Deprecated
|
||||
``autodoc.DocstringSignatureMixin.get_doc()``,
|
||||
``autodoc.DocstringSignatureMixin._find_signature()``, and
|
||||
``autodoc.ClassDocumenter.get_doc()`` are deprecated.
|
||||
* The ``importer`` argument of ``sphinx.ext.autodoc.importer._MockModule``
|
||||
* The ``nodetype`` argument of ``sphinx.search.WordCollector.
|
||||
is_meta_keywords()``
|
||||
* The ``suffix`` argument of ``env.doc2path()`` is deprecated.
|
||||
* The string style ``base`` argument of ``env.doc2path()`` is deprecated.
|
||||
* ``sphinx.application.Sphinx._setting_up_extension``
|
||||
* ``sphinx.ext.config.check_unicode()``
|
||||
* ``sphinx.ext.autodoc.importer._MockImporter``
|
||||
* ``sphinx.ext.doctest.doctest_encode()``
|
||||
* ``sphinx.testing.util.remove_unicode_literal()``
|
||||
* ``sphinx.util.force_decode()``
|
||||
|
@ -187,6 +187,16 @@ The following is a list of deprecated interfaces.
|
||||
- 3.0
|
||||
- N/A
|
||||
|
||||
* - The ``importer`` argument of ``sphinx.ext.autodoc.importer._MockModule``
|
||||
- 2.0
|
||||
- 3.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.ext.autodoc.importer._MockImporter``
|
||||
- 2.0
|
||||
- 3.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.writers.latex.LaTeXTranslator._make_visit_admonition()``
|
||||
- 2.0
|
||||
- 3.0
|
||||
|
@ -14,14 +14,17 @@ import sys
|
||||
import traceback
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
from importlib.abc import Loader, MetaPathFinder
|
||||
from importlib.machinery import ModuleSpec
|
||||
from types import FunctionType, MethodType, ModuleType
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.inspect import isenumclass, safe_getattr
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple # NOQA
|
||||
from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Sequence, Tuple, Union # NOQA
|
||||
from sphinx.util.typing import unicode # NOQA
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -78,13 +81,16 @@ class _MockModule(ModuleType):
|
||||
"""Used by autodoc_mock_imports."""
|
||||
__file__ = '/dev/null'
|
||||
|
||||
def __init__(self, name, loader):
|
||||
def __init__(self, name, loader=None):
|
||||
# type: (str, _MockImporter) -> None
|
||||
self.__name__ = self.__package__ = name
|
||||
self.__loader__ = loader
|
||||
super(_MockModule, self).__init__(name)
|
||||
self.__all__ = [] # type: List[str]
|
||||
self.__path__ = [] # type: List[str]
|
||||
|
||||
if loader is not None:
|
||||
warnings.warn('The loader argument for _MockModule is deprecated.',
|
||||
RemovedInSphinx30Warning)
|
||||
|
||||
def __getattr__(self, name):
|
||||
# type: (str) -> _MockObject
|
||||
o = _MockObject()
|
||||
@ -100,6 +106,9 @@ class _MockImporter:
|
||||
# enable hook by adding itself to meta_path
|
||||
sys.meta_path.insert(0, self)
|
||||
|
||||
warnings.warn('_MockImporter is now deprecated.',
|
||||
RemovedInSphinx30Warning)
|
||||
|
||||
def disable(self):
|
||||
# type: () -> None
|
||||
# remove `self` from `sys.meta_path` to disable import hook
|
||||
@ -131,14 +140,66 @@ class _MockImporter:
|
||||
return module
|
||||
|
||||
|
||||
class MockLoader(Loader):
|
||||
"""A loader for mocking."""
|
||||
def __init__(self, finder):
|
||||
# type: (MockFinder) -> None
|
||||
super(MockLoader, self).__init__()
|
||||
self.finder = finder
|
||||
|
||||
def create_module(self, spec):
|
||||
# type: (ModuleSpec) -> ModuleType
|
||||
logger.debug('[autodoc] adding a mock module as %s!', spec.name)
|
||||
self.finder.mocked_modules.append(spec.name)
|
||||
return _MockModule(spec.name)
|
||||
|
||||
def exec_module(self, module):
|
||||
# type: (ModuleType) -> None
|
||||
pass # nothing to do
|
||||
|
||||
|
||||
class MockFinder(MetaPathFinder):
|
||||
"""A finder for mocking."""
|
||||
|
||||
def __init__(self, modnames):
|
||||
# type: (List[str]) -> None
|
||||
super(MockFinder, self).__init__()
|
||||
self.modnames = modnames
|
||||
self.loader = MockLoader(self)
|
||||
self.mocked_modules = [] # type: List[str]
|
||||
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
# type: (str, Sequence[Union[bytes, str]], ModuleType) -> ModuleSpec
|
||||
for modname in self.modnames:
|
||||
# check if fullname is (or is a descendant of) one of our targets
|
||||
if modname == fullname or fullname.startswith(modname + '.'):
|
||||
return ModuleSpec(fullname, self.loader)
|
||||
|
||||
return None
|
||||
|
||||
def invalidate_caches(self):
|
||||
# type: () -> None
|
||||
"""Invalidate mocked modules on sys.modules."""
|
||||
for modname in self.mocked_modules:
|
||||
sys.modules.pop(modname, None)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mock(names):
|
||||
# type: (List[str]) -> Generator
|
||||
def mock(modnames):
|
||||
# type: (List[str]) -> Generator[None, None, None]
|
||||
"""Insert mock modules during context::
|
||||
|
||||
with mock(['target.module.name']):
|
||||
# mock modules are enabled here
|
||||
...
|
||||
"""
|
||||
try:
|
||||
importer = _MockImporter(names)
|
||||
finder = MockFinder(modnames)
|
||||
sys.meta_path.insert(0, finder)
|
||||
yield
|
||||
finally:
|
||||
importer.disable()
|
||||
sys.meta_path.remove(finder)
|
||||
finder.invalidate_caches()
|
||||
|
||||
|
||||
def import_module(modname, warningiserror=False):
|
||||
|
Loading…
Reference in New Issue
Block a user