ansible-later/env_27/lib/python2.7/site-packages/pytest_mock.py

337 lines
11 KiB
Python
Raw Normal View History

2019-04-11 13:56:20 +00:00
from __future__ import unicode_literals
import inspect
import sys
import pytest
from _pytest_mock_version import version
__version__ = version
# pseudo-six; if this starts to require more than this, depend on six already
if sys.version_info[0] == 2: # pragma: no cover
text_type = unicode # noqa
else:
text_type = str
def _get_mock_module(config):
"""
Import and return the actual "mock" module. By default this is "mock" for Python 2 and
"unittest.mock" for Python 3, but the user can force to always use "mock" on Python 3 using
the mock_use_standalone_module ini option.
"""
if not hasattr(_get_mock_module, "_module"):
use_standalone_module = parse_ini_boolean(
config.getini("mock_use_standalone_module")
)
if sys.version_info[0] == 2 or use_standalone_module:
import mock
_get_mock_module._module = mock
else:
import unittest.mock
_get_mock_module._module = unittest.mock
return _get_mock_module._module
class MockFixture(object):
"""
Fixture that provides the same interface to functions in the mock module,
ensuring that they are uninstalled at the end of each test.
"""
def __init__(self, config):
self._patches = [] # list of mock._patch objects
self._mocks = [] # list of MagicMock objects
self.mock_module = mock_module = _get_mock_module(config)
self.patch = self._Patcher(self._patches, self._mocks, mock_module)
# aliases for convenience
self.Mock = mock_module.Mock
self.MagicMock = mock_module.MagicMock
self.NonCallableMock = mock_module.NonCallableMock
self.PropertyMock = mock_module.PropertyMock
self.call = mock_module.call
self.ANY = mock_module.ANY
self.DEFAULT = mock_module.DEFAULT
self.create_autospec = mock_module.create_autospec
self.sentinel = mock_module.sentinel
self.mock_open = mock_module.mock_open
def resetall(self):
"""
Call reset_mock() on all patchers started by this fixture.
"""
for m in self._mocks:
m.reset_mock()
def stopall(self):
"""
Stop all patchers started by this fixture. Can be safely called multiple
times.
"""
for p in reversed(self._patches):
p.stop()
self._patches[:] = []
self._mocks[:] = []
def spy(self, obj, name):
"""
Creates a spy of method. It will run method normally, but it is now
possible to use `mock` call features with it, like call count.
:param object obj: An object.
:param unicode name: A method in object.
:rtype: mock.MagicMock
:return: Spy object.
"""
method = getattr(obj, name)
autospec = inspect.ismethod(method) or inspect.isfunction(method)
# Can't use autospec classmethod or staticmethod objects
# see: https://bugs.python.org/issue23078
if inspect.isclass(obj):
# Bypass class descriptor:
# http://stackoverflow.com/questions/14187973/python3-check-if-method-is-static
try:
value = obj.__getattribute__(obj, name)
except AttributeError:
pass
else:
if isinstance(value, (classmethod, staticmethod)):
autospec = False
result = self.patch.object(obj, name, side_effect=method, autospec=autospec)
return result
def stub(self, name=None):
"""
Creates a stub method. It accepts any arguments. Ideal to register to
callbacks in tests.
:param name: the constructed stub's name as used in repr
:rtype: mock.MagicMock
:return: Stub object.
"""
return self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name)
class _Patcher(object):
"""
Object to provide the same interface as mock.patch, mock.patch.object,
etc. We need this indirection to keep the same API of the mock package.
"""
def __init__(self, patches, mocks, mock_module):
self._patches = patches
self._mocks = mocks
self.mock_module = mock_module
def _start_patch(self, mock_func, *args, **kwargs):
"""Patches something by calling the given function from the mock
module, registering the patch to stop it later and returns the
mock object resulting from the mock call.
"""
p = mock_func(*args, **kwargs)
mocked = p.start()
self._patches.append(p)
if hasattr(mocked, "reset_mock"):
self._mocks.append(mocked)
return mocked
def object(self, *args, **kwargs):
"""API to mock.patch.object"""
return self._start_patch(self.mock_module.patch.object, *args, **kwargs)
def multiple(self, *args, **kwargs):
"""API to mock.patch.multiple"""
return self._start_patch(self.mock_module.patch.multiple, *args, **kwargs)
def dict(self, *args, **kwargs):
"""API to mock.patch.dict"""
return self._start_patch(self.mock_module.patch.dict, *args, **kwargs)
def __call__(self, *args, **kwargs):
"""API to mock.patch"""
return self._start_patch(self.mock_module.patch, *args, **kwargs)
@pytest.yield_fixture
def mocker(pytestconfig):
"""
return an object that has the same interface to the `mock` module, but
takes care of automatically undoing all patches after each test method.
"""
result = MockFixture(pytestconfig)
yield result
result.stopall()
@pytest.fixture
def mock(mocker):
"""
Same as "mocker", but kept only for backward compatibility.
"""
import warnings
warnings.warn(
'"mock" fixture has been deprecated, use "mocker" instead', DeprecationWarning
)
return mocker
_mock_module_patches = []
_mock_module_originals = {}
def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
return
except AssertionError as e:
if getattr(e, "_mock_introspection_applied", 0):
msg = text_type(e)
else:
__mock_self = args[0]
msg = text_type(e)
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
msg += "\n\npytest introspection follows:\n"
try:
assert actual_args == args[1:]
except AssertionError as e:
msg += "\nArgs:\n" + text_type(e)
try:
assert actual_kwargs == kwargs
except AssertionError as e:
msg += "\nKwargs:\n" + text_type(e)
e = AssertionError(msg)
e._mock_introspection_applied = True
raise e
def wrap_assert_not_called(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_not_called"], *args, **kwargs)
def wrap_assert_called_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_with"], *args, **kwargs)
def wrap_assert_called_once(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_once"], *args, **kwargs)
def wrap_assert_called_once_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_once_with"], *args, **kwargs)
def wrap_assert_has_calls(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_has_calls"], *args, **kwargs)
def wrap_assert_any_call(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_any_call"], *args, **kwargs)
def wrap_assert_called(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called"], *args, **kwargs)
def wrap_assert_methods(config):
"""
Wrap assert methods of mock module so we can hide their traceback and
add introspection information to specified argument asserts.
"""
# Make sure we only do this once
if _mock_module_originals:
return
mock_module = _get_mock_module(config)
wrappers = {
"assert_called": wrap_assert_called,
"assert_called_once": wrap_assert_called_once,
"assert_called_with": wrap_assert_called_with,
"assert_called_once_with": wrap_assert_called_once_with,
"assert_any_call": wrap_assert_any_call,
"assert_has_calls": wrap_assert_has_calls,
"assert_not_called": wrap_assert_not_called,
}
for method, wrapper in wrappers.items():
try:
original = getattr(mock_module.NonCallableMock, method)
except AttributeError: # pragma: no cover
continue
_mock_module_originals[method] = original
patcher = mock_module.patch.object(mock_module.NonCallableMock, method, wrapper)
patcher.start()
_mock_module_patches.append(patcher)
if hasattr(config, "add_cleanup"):
add_cleanup = config.add_cleanup
else:
# pytest 2.7 compatibility
add_cleanup = config._cleanup.append
add_cleanup(unwrap_assert_methods)
def unwrap_assert_methods():
for patcher in _mock_module_patches:
try:
patcher.stop()
except RuntimeError as e:
# a patcher might have been stopped by user code (#137)
# so we need to catch this error here and ignore it;
# unfortunately there's no public API to check if a patch
# has been started, so catching the error it is
if text_type(e) == "stop called on unstarted patcher":
pass
else:
raise
_mock_module_patches[:] = []
_mock_module_originals.clear()
def pytest_addoption(parser):
parser.addini(
"mock_traceback_monkeypatch",
"Monkeypatch the mock library to improve reporting of the "
"assert_called_... methods",
default=True,
)
parser.addini(
"mock_use_standalone_module",
'Use standalone "mock" (from PyPI) instead of builtin "unittest.mock" '
"on Python 3",
default=False,
)
def parse_ini_boolean(value):
if value in (True, False):
return value
try:
return {"true": True, "false": False}[value.lower()]
except KeyError:
raise ValueError("unknown string for bool: %r" % value)
def pytest_configure(config):
tb = config.getoption("--tb")
if (
parse_ini_boolean(config.getini("mock_traceback_monkeypatch"))
and tb != "native"
):
wrap_assert_methods(config)