summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-05-15 18:17:41 +0300
committerrobot-piglet <[email protected]>2026-05-15 18:40:10 +0300
commita666fd3e1cbef934f3b4cc8cb39b09876b497948 (patch)
tree4407ad5979e269c37b76d2b43b20cb313b662014 /contrib/python
parent3a786a661247268bf50c55d997cfa2aa05a8e4d2 (diff)
Intermediate changes
commit_hash:8507ea815acd7b5ccd20d86ddbd797183ff2f5c9
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/mock/py3/.dist-info/METADATA30
-rw-r--r--contrib/python/mock/py3/mock/__init__.py2
-rw-r--r--contrib/python/mock/py3/mock/backports.py25
-rw-r--r--contrib/python/mock/py3/mock/mock.py529
-rw-r--r--contrib/python/mock/py3/ya.make2
5 files changed, 467 insertions, 121 deletions
diff --git a/contrib/python/mock/py3/.dist-info/METADATA b/contrib/python/mock/py3/.dist-info/METADATA
index 53aa68bc730..7b440edc89a 100644
--- a/contrib/python/mock/py3/.dist-info/METADATA
+++ b/contrib/python/mock/py3/.dist-info/METADATA
@@ -1,12 +1,11 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.2
Name: mock
-Version: 4.0.3
+Version: 5.2.0
Summary: Rolling backport of unittest.mock for all Pythons
Home-page: http://mock.readthedocs.org/en/latest/
Author: Testing Cabal
Author-email: [email protected]
-License: UNKNOWN
-Platform: UNKNOWN
+Project-URL: Source, https://github.com/testing-cabal/mock
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
@@ -16,21 +15,28 @@ Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.6
-Provides-Extra: build
-Requires-Dist: twine ; extra == 'build'
-Requires-Dist: wheel ; extra == 'build'
-Requires-Dist: blurb ; extra == 'build'
+License-File: LICENSE.txt
Provides-Extra: docs
-Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: sphinx; extra == "docs"
Provides-Extra: test
-Requires-Dist: pytest (<5.4) ; extra == 'test'
-Requires-Dist: pytest-cov ; extra == 'test'
+Requires-Dist: pytest; extra == "test"
+Requires-Dist: pytest-cov; extra == "test"
+Provides-Extra: build
+Requires-Dist: twine; extra == "build"
+Requires-Dist: wheel; extra == "build"
+Requires-Dist: blurb; extra == "build"
+Dynamic: description
mock is a library for testing in Python. It allows you to replace parts of
your system under test with mock objects and make assertions about how they
@@ -68,5 +74,3 @@ Please see the standard library documentation for more details.
.. _BSD License: https://github.com/testing-cabal/mock/blob/master/LICENSE.txt
.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html
.. _mock on PyPI: https://pypi.org/project/mock/
-
-
diff --git a/contrib/python/mock/py3/mock/__init__.py b/contrib/python/mock/py3/mock/__init__.py
index dbe8031ba3f..055601d814c 100644
--- a/contrib/python/mock/py3/mock/__init__.py
+++ b/contrib/python/mock/py3/mock/__init__.py
@@ -7,7 +7,7 @@ IS_PYPY = 'PyPy' in sys.version
import mock.mock as _mock
from mock.mock import *
-__version__ = '4.0.3'
+__version__ = '5.2.0'
version_info = tuple(int(p) for p in
re.match(r'(\d+).(\d+).(\d+)', __version__).groups())
diff --git a/contrib/python/mock/py3/mock/backports.py b/contrib/python/mock/py3/mock/backports.py
index 6f20494c94f..598a512b487 100644
--- a/contrib/python/mock/py3/mock/backports.py
+++ b/contrib/python/mock/py3/mock/backports.py
@@ -1,12 +1,15 @@
import sys
-if sys.version_info[:2] < (3, 8):
+if sys.version_info[:2] > (3, 9):
+ from inspect import iscoroutinefunction
+elif sys.version_info[:2] >= (3, 8):
+ from asyncio import iscoroutinefunction
+else:
- import asyncio, functools
+ import functools
from asyncio.coroutines import _is_coroutine
from inspect import ismethod, isfunction, CO_COROUTINE
- from unittest import TestCase
def _unwrap_partial(func):
while isinstance(func, functools.partial):
@@ -35,6 +38,13 @@ if sys.version_info[:2] < (3, 8):
)
+try:
+ from unittest import IsolatedAsyncioTestCase
+except ImportError:
+ import asyncio
+ from unittest import TestCase
+
+
class IsolatedAsyncioTestCase(TestCase):
def __init__(self, methodName='runTest'):
@@ -82,8 +92,7 @@ if sys.version_info[:2] < (3, 8):
self._tearDownAsyncioLoop()
-else:
-
- from asyncio import iscoroutinefunction
- from unittest import IsolatedAsyncioTestCase
-
+try:
+ from asyncio import _set_event_loop_policy as set_event_loop_policy
+except ImportError:
+ from asyncio import set_event_loop_policy
diff --git a/contrib/python/mock/py3/mock/mock.py b/contrib/python/mock/py3/mock/mock.py
index 6ba80d7cb22..236fcac77ea 100644
--- a/contrib/python/mock/py3/mock/mock.py
+++ b/contrib/python/mock/py3/mock/mock.py
@@ -14,6 +14,7 @@ __all__ = (
'call',
'create_autospec',
'AsyncMock',
+ 'ThreadingMock',
'FILTER_DIR',
'NonCallableMock',
'NonCallableMagicMock',
@@ -29,15 +30,29 @@ import io
import inspect
import pprint
import sys
+import threading
import builtins
-from asyncio import iscoroutinefunction
+
+try:
+ from dataclasses import fields, is_dataclass
+ HAS_DATACLASSES = True
+except ImportError:
+ HAS_DATACLASSES = False
+
from types import CodeType, ModuleType, MethodType
from unittest.util import safe_repr
from functools import wraps, partial
+from threading import RLock
+
from mock import IS_PYPY
from .backports import iscoroutinefunction
+
+class InvalidSpecError(Exception):
+ """Indicates that an invalid value was used as a mock spec."""
+
+
_builtins = {name for name in dir(builtins) if not name.startswith('_')}
FILTER_DIR = True
@@ -94,6 +109,12 @@ def _get_signature_object(func, as_instance, eat_self):
func = func.__init__
# Skip the `self` argument in __init__
eat_self = True
+ elif isinstance(func, (classmethod, staticmethod)):
+ if isinstance(func, classmethod):
+ # Skip the `cls` argument of a class method
+ eat_self = True
+ # Use the original decorated method to extract the correct function signature
+ func = func.__func__
elif not isinstance(func, FunctionTypes):
# If we really want to model an instance of the passed type,
# __call__ should be looked up, not __init__.
@@ -194,6 +215,28 @@ def _set_signature(mock, original, instance=False):
_setup_func(funcopy, mock, sig)
return funcopy
+def _set_async_signature(mock, original, instance=False, is_async_mock=False):
+ # creates an async function with signature (*args, **kwargs) that delegates to a
+ # mock. It still does signature checking by calling a lambda with the same
+ # signature as the original.
+
+ skipfirst = isinstance(original, type)
+ func, sig = _get_signature_object(original, instance, skipfirst)
+ def checksig(*args, **kwargs):
+ sig.bind(*args, **kwargs)
+ _copy_func_details(func, checksig)
+
+ name = original.__name__
+ context = {'_checksig_': checksig, 'mock': mock}
+ src = """async def %s(*args, **kwargs):
+ _checksig_(*args, **kwargs)
+ return await mock(*args, **kwargs)""" % name
+ exec (src, context)
+ funcopy = context[name]
+ _setup_func(funcopy, mock, sig)
+ _setup_async_mock(funcopy)
+ return funcopy
+
def _setup_func(funcopy, mock, sig):
funcopy.mock = mock
@@ -399,15 +442,26 @@ class Base(object):
class NonCallableMock(Base):
"""A non-callable version of `Mock`"""
- def __new__(cls, *args, **kw):
+ # Store a mutex as a class attribute in order to protect concurrent access
+ # to mock attributes. Using a class attribute allows all NonCallableMock
+ # instances to share the mutex for simplicity.
+ #
+ # See https://github.com/python/cpython/issues/98624 for why this is
+ # necessary.
+ _lock = RLock()
+
+ def __new__(
+ cls, spec=None, wraps=None, name=None, spec_set=None,
+ parent=None, _spec_state=None, _new_name='', _new_parent=None,
+ _spec_as_instance=False, _eat_self=None, unsafe=False, **kwargs
+ ):
# every instance has its own class
# so we can create magic methods on the
# class without stomping on other mocks
bases = (cls,)
if not issubclass(cls, AsyncMockMixin):
# Check if spec is an async object or function
- bound_args = _MOCK_SIG.bind_partial(cls, *args, **kw).arguments
- spec_arg = bound_args.get('spec_set', bound_args.get('spec'))
+ spec_arg = spec_set or spec
if spec_arg is not None and _is_async_obj(spec_arg):
bases = (AsyncMockMixin, cls)
new = type(cls.__name__, bases, {'__doc__': cls.__doc__})
@@ -486,14 +540,13 @@ class NonCallableMock(Base):
def _mock_add_spec(self, spec, spec_set, _spec_as_instance=False,
_eat_self=False):
+ if _is_instance_mock(spec):
+ raise InvalidSpecError(f'Cannot spec a Mock object. [object={spec!r}]')
+
_spec_class = None
_spec_signature = None
_spec_asyncs = []
- for attr in dir(spec):
- if iscoroutinefunction(getattr(spec, attr, None)):
- _spec_asyncs.append(attr)
-
if spec is not None and not _is_list(spec):
if isinstance(spec, type):
_spec_class = spec
@@ -503,7 +556,19 @@ class NonCallableMock(Base):
_spec_as_instance, _eat_self)
_spec_signature = res and res[1]
- spec = dir(spec)
+ spec_list = dir(spec)
+
+ for attr in spec_list:
+ static_attr = inspect.getattr_static(spec, attr, None)
+ unwrapped_attr = static_attr
+ try:
+ unwrapped_attr = inspect.unwrap(unwrapped_attr)
+ except ValueError:
+ pass
+ if iscoroutinefunction(unwrapped_attr):
+ _spec_asyncs.append(attr)
+
+ spec = spec_list
__dict__ = self.__dict__
__dict__['_spec_class'] = _spec_class
@@ -517,7 +582,7 @@ class NonCallableMock(Base):
if self._mock_delegate is not None:
ret = self._mock_delegate.return_value
- if ret is DEFAULT:
+ if ret is DEFAULT and self._mock_wraps is None:
ret = self._get_child_mock(
_new_parent=self, _new_name='()'
)
@@ -572,7 +637,9 @@ class NonCallableMock(Base):
side_effect = property(__get_side_effect, __set_side_effect)
- def reset_mock(self, visited=None,*, return_value=False, side_effect=False):
+ def reset_mock(self, visited=None, *,
+ return_value: bool = False,
+ side_effect: bool = False):
"Restore the mock object to its initial state."
if visited is None:
visited = []
@@ -632,33 +699,42 @@ class NonCallableMock(Base):
raise AttributeError("Mock object has no attribute %r" % name)
elif _is_magic(name):
raise AttributeError(name)
- if not self._mock_unsafe:
- if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')):
- raise AttributeError("Attributes cannot start with 'assert' "
- "or its misspellings")
+ if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods):
+ if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in _ATTRIB_DENY_LIST:
+ raise AttributeError(
+ f"{name!r} is not a valid assertion. Use a spec "
+ f"for the mock if {name!r} is meant to be an attribute.")
- result = self._mock_children.get(name)
- if result is _deleted:
- raise AttributeError(name)
- elif result is None:
- wraps = None
- if self._mock_wraps is not None:
- # XXXX should we get the attribute without triggering code
- # execution?
- wraps = getattr(self._mock_wraps, name)
+ with NonCallableMock._lock:
+ result = self._mock_children.get(name)
+ if result is _deleted:
+ raise AttributeError(name)
+ elif result is None:
+ wraps = None
+ if self._mock_wraps is not None:
+ # XXXX should we get the attribute without triggering code
+ # execution?
+ wraps = getattr(self._mock_wraps, name)
- result = self._get_child_mock(
- parent=self, name=name, wraps=wraps, _new_name=name,
- _new_parent=self
- )
- self._mock_children[name] = result
+ result = self._get_child_mock(
+ parent=self, name=name, wraps=wraps, _new_name=name,
+ _new_parent=self
+ )
+ self._mock_children[name] = result
- elif isinstance(result, _SpecState):
- result = create_autospec(
- result.spec, result.spec_set, result.instance,
- result.parent, result.name
- )
- self._mock_children[name] = result
+ elif isinstance(result, _SpecState):
+ try:
+ result = create_autospec(
+ result.spec, result.spec_set, result.instance,
+ result.parent, result.name
+ )
+ except InvalidSpecError:
+ target_name = self.__dict__['_mock_name'] or self
+ raise InvalidSpecError(
+ f'Cannot autospec attr {name!r} from target '
+ f'{target_name!r} as it has already been mocked out. '
+ f'[target={self!r}, attr={result.spec!r}]')
+ self._mock_children[name] = result
return result
@@ -765,6 +841,9 @@ class NonCallableMock(Base):
mock_name = f'{self._extract_mock_name()}.{name}'
raise AttributeError(f'Cannot set {mock_name}')
+ if isinstance(value, PropertyMock):
+ self.__dict__[name] = value
+ return
return object.__setattr__(self, name, value)
@@ -792,7 +871,7 @@ class NonCallableMock(Base):
def _format_mock_failure_message(self, args, kwargs, action='call'):
- message = 'expected %s not found.\nExpected: %s\nActual: %s'
+ message = 'expected %s not found.\nExpected: %s\n Actual: %s'
expected_string = self._format_mock_call_signature(args, kwargs)
call_args = self.call_args
actual_string = self._format_mock_call_signature(*call_args)
@@ -899,7 +978,7 @@ class NonCallableMock(Base):
if self.call_args is None:
expected = self._format_mock_call_signature(args, kwargs)
actual = 'not called.'
- error_message = ('expected call not found.\nExpected: %s\nActual: %s'
+ error_message = ('expected call not found.\nExpected: %s\n Actual: %s'
% (expected, actual))
raise AssertionError(error_message)
@@ -950,8 +1029,8 @@ class NonCallableMock(Base):
for e in expected])
raise AssertionError(
f'{problem}\n'
- f'Expected: {_CallList(calls)}'
- f'{self._calls_repr(prefix="Actual").rstrip(".")}'
+ f'Expected: {_CallList(calls)}\n'
+ f' Actual: {safe_repr(self.mock_calls)}'
) from cause
return
@@ -995,6 +1074,11 @@ class NonCallableMock(Base):
For non-callable mocks the callable variant will be used (rather than
any custom subclass)."""
+ if self._mock_sealed:
+ attribute = f".{kw['name']}" if "name" in kw else "()"
+ mock_name = self._extract_mock_name() + attribute
+ raise AttributeError(mock_name)
+
_new_name = kw.get("_new_name")
if _new_name in self.__dict__['_spec_asyncs']:
return AsyncMock(**kw)
@@ -1017,16 +1101,10 @@ class NonCallableMock(Base):
klass = Mock
else:
klass = _type.__mro__[1]
-
- if self._mock_sealed:
- attribute = "." + kw["name"] if "name" in kw else "()"
- mock_name = self._extract_mock_name() + attribute
- raise AttributeError(mock_name)
-
return klass(**kw)
- def _calls_repr(self, prefix="Calls"):
+ def _calls_repr(self):
"""Renders self.mock_calls as a string.
Example: "\nCalls: [call(1), call(2)]."
@@ -1036,10 +1114,22 @@ class NonCallableMock(Base):
"""
if not self.mock_calls:
return ""
- return f"\n{prefix}: {safe_repr(self.mock_calls)}."
+ return f"\nCalls: {safe_repr(self.mock_calls)}."
+
+try:
+ removeprefix = str.removeprefix
+except AttributeError:
+ # Py 3.8 and earlier:
+ def removeprefix(name, prefix):
+ return name[len(prefix):]
-_MOCK_SIG = inspect.signature(NonCallableMock.__init__)
+# Denylist for forbidden attribute names in safe mode
+_ATTRIB_DENY_LIST = frozenset({
+ removeprefix(name, "assert_")
+ for name in dir(NonCallableMock)
+ if name.startswith("assert_")
+})
class _AnyComparer(list):
@@ -1172,6 +1262,9 @@ class CallableMixin(Base):
if self._mock_return_value is not DEFAULT:
return self.return_value
+ if self._mock_delegate and self._mock_delegate.return_value is not DEFAULT:
+ return self.return_value
+
if self._mock_wraps is not None:
return self._mock_wraps(*args, **kwargs)
@@ -1212,6 +1305,13 @@ class Mock(CallableMixin, NonCallableMock):
this is a new Mock (created on first access). See the
`return_value` attribute.
+ * `unsafe`: By default, accessing any attribute whose name starts with
+ *assert*, *assret*, *asert*, *aseert*, or *assrt* raises an AttributeError.
+ Additionally, an AttributeError is raised when accessing
+ attributes that match the name of an assertion method without the prefix
+ `assert_`, e.g. accessing `called_once` instead of `assert_called_once`.
+ Passing `unsafe=True` will allow access to these attributes.
+
* `wraps`: Item for the mock object to wrap. If `wraps` is not None then
calling the Mock will pass the call through to the wrapped object
(returning the real result). Attribute access on the mock will return a
@@ -1250,6 +1350,17 @@ def _importer(target):
return thing
+# _check_spec_arg_typos takes kwargs from commands like patch and checks that
+# they don't contain common misspellings of arguments related to autospeccing.
+def _check_spec_arg_typos(kwargs_to_check):
+ typos = ("autospect", "auto_spec", "set_spec")
+ for typo in typos:
+ if typo in kwargs_to_check:
+ raise RuntimeError(
+ f"{typo!r} might be a typo; use unsafe=True if this is intended"
+ )
+
+
class _patch(object):
attribute_name = None
@@ -1257,7 +1368,7 @@ class _patch(object):
def __init__(
self, getter, attribute, new, spec, create,
- spec_set, autospec, new_callable, kwargs
+ spec_set, autospec, new_callable, kwargs, *, unsafe=False
):
if new_callable is not None:
if new is not DEFAULT:
@@ -1268,6 +1379,16 @@ class _patch(object):
raise ValueError(
"Cannot use 'autospec' and 'new_callable' together"
)
+ if not unsafe:
+ _check_spec_arg_typos(kwargs)
+ if _is_instance_mock(spec):
+ raise InvalidSpecError(
+ f'Cannot spec attr {attribute!r} as the spec '
+ f'has already been mocked out. [spec={spec!r}]')
+ if _is_instance_mock(spec_set):
+ raise InvalidSpecError(
+ f'Cannot spec attr {attribute!r} as the spec_set '
+ f'target has already been mocked out. [spec_set={spec_set!r}]')
self.getter = getter
self.attribute = attribute
@@ -1280,6 +1401,7 @@ class _patch(object):
self.autospec = autospec
self.kwargs = kwargs
self.additional_patchers = []
+ self.is_started = False
def copy(self):
@@ -1298,7 +1420,7 @@ class _patch(object):
def __call__(self, func):
if isinstance(func, type):
return self.decorate_class(func)
- if inspect.iscoroutinefunction(func):
+ if iscoroutinefunction(func):
return self.decorate_async_callable(func)
return self.decorate_callable(func)
@@ -1392,6 +1514,9 @@ class _patch(object):
def __enter__(self):
"""Perform the patch."""
+ if self.is_started:
+ raise RuntimeError("Patch is already started")
+
new, spec, spec_set = self.new, self.spec, self.spec_set
autospec, kwargs = self.autospec, self.kwargs
new_callable = self.new_callable
@@ -1434,13 +1559,12 @@ class _patch(object):
if isinstance(original, type):
# If we're patching out a class and there is a spec
inherit = True
- if spec is None and _is_async_obj(original):
- Klass = AsyncMock
- else:
- Klass = MagicMock
- _kwargs = {}
+
+ # Determine the Klass to use
if new_callable is not None:
Klass = new_callable
+ elif spec is None and _is_async_obj(original):
+ Klass = AsyncMock
elif spec is not None or spec_set is not None:
this_spec = spec
if spec_set is not None:
@@ -1453,7 +1577,12 @@ class _patch(object):
Klass = AsyncMock
elif not_callable:
Klass = NonCallableMagicMock
+ else:
+ Klass = MagicMock
+ else:
+ Klass = MagicMock
+ _kwargs = {}
if spec is not None:
_kwargs['spec'] = spec
if spec_set is not None:
@@ -1495,6 +1624,18 @@ class _patch(object):
if autospec is True:
autospec = original
+ if _is_instance_mock(self.target):
+ raise InvalidSpecError(
+ f'Cannot autospec attr {self.attribute!r} as the patch '
+ f'target has already been mocked out. '
+ f'[target={self.target!r}, attr={autospec!r}]')
+ if _is_instance_mock(autospec):
+ target_name = getattr(self.target, '__name__', self.target)
+ raise InvalidSpecError(
+ f'Cannot autospec attr {self.attribute!r} from target '
+ f'{target_name!r} as it has already been mocked out. '
+ f'[target={self.target!r}, attr={autospec!r}]')
+
new = create_autospec(autospec, spec_set=spec_set,
_name=self.attribute, **kwargs)
elif kwargs:
@@ -1507,6 +1648,7 @@ class _patch(object):
self.temp_original = original
self.is_local = local
self._exit_stack = contextlib.ExitStack()
+ self.is_started = True
try:
setattr(self.target, self.attribute, new_attr)
if self.attribute_name is not None:
@@ -1526,6 +1668,9 @@ class _patch(object):
def __exit__(self, *exc_info):
"""Undo the patch."""
+ if not self.is_started:
+ return
+
if self.is_local and self.temp_original is not DEFAULT:
setattr(self.target, self.attribute, self.temp_original)
else:
@@ -1542,6 +1687,7 @@ class _patch(object):
del self.target
exit_stack = self._exit_stack
del self._exit_stack
+ self.is_started = False
return exit_stack.__exit__(*exc_info)
@@ -1567,9 +1713,9 @@ class _patch(object):
def _get_target(target):
try:
target, attribute = target.rsplit('.', 1)
- except (TypeError, ValueError):
- raise TypeError("Need a valid target to patch. You supplied: %r" %
- (target,))
+ except (TypeError, ValueError, AttributeError):
+ raise TypeError(
+ f"Need a valid target to patch. You supplied: {target!r}")
getter = lambda: _importer(target)
return getter, attribute
@@ -1577,7 +1723,7 @@ def _get_target(target):
def _patch_object(
target, attribute, new=DEFAULT, spec=None,
create=False, spec_set=None, autospec=None,
- new_callable=None, **kwargs
+ new_callable=None, *, unsafe=False, **kwargs
):
"""
patch the named member (`attribute`) on an object (`target`) with a mock
@@ -1599,7 +1745,7 @@ def _patch_object(
getter = lambda: target
return _patch(
getter, attribute, new, spec, create,
- spec_set, autospec, new_callable, kwargs
+ spec_set, autospec, new_callable, kwargs, unsafe=unsafe
)
@@ -1654,7 +1800,7 @@ def _patch_multiple(target, spec=None, create=False, spec_set=None,
def patch(
target, new=DEFAULT, spec=None, create=False,
- spec_set=None, autospec=None, new_callable=None, **kwargs
+ spec_set=None, autospec=None, new_callable=None, *, unsafe=False, **kwargs
):
"""
`patch` acts as a function decorator, class decorator or a context
@@ -1663,7 +1809,7 @@ def patch(
the patch is undone.
If `new` is omitted, then the target is replaced with an
- `AsyncMock if the patched object is an async function or a
+ `AsyncMock` if the patched object is an async function or a
`MagicMock` otherwise. If `patch` is used as a decorator and `new` is
omitted, the created mock is passed in as an extra argument to the
decorated function. If `patch` is used as a context manager the created
@@ -1716,6 +1862,10 @@ def patch(
use "as" then the patched object will be bound to the name after the
"as"; very useful if `patch` is creating a mock object for you.
+ Patch will raise a `RuntimeError` if passed some common misspellings of
+ the arguments autospec and spec_set. Pass the argument `unsafe` with the
+ value True to disable that check.
+
`patch` takes arbitrary keyword arguments. These will be passed to
`AsyncMock` if the patched object is asynchronous, to `MagicMock`
otherwise or to `new_callable` if specified.
@@ -1726,14 +1876,15 @@ def patch(
getter, attribute = _get_target(target)
return _patch(
getter, attribute, new, spec, create,
- spec_set, autospec, new_callable, kwargs
+ spec_set, autospec, new_callable, kwargs, unsafe=unsafe
)
class _patch_dict(object):
"""
Patch a dictionary, or dictionary like object, and restore the dictionary
- to its original state after the test.
+ to its original state after the test, where the restored dictionary is
+ a copy of the dictionary as it was before the test.
`in_dict` can be a dictionary or a mapping like container. If it is a
mapping then it must at least support getting, setting and deleting items
@@ -1771,6 +1922,12 @@ class _patch_dict(object):
def __call__(self, f):
if isinstance(f, type):
return self.decorate_class(f)
+ if iscoroutinefunction(f):
+ return self.decorate_async_callable(f)
+ return self.decorate_callable(f)
+
+
+ def decorate_callable(self, f):
@wraps(f)
def _inner(*args, **kw):
self._patch_dict()
@@ -1782,6 +1939,18 @@ class _patch_dict(object):
return _inner
+ def decorate_async_callable(self, f):
+ @wraps(f)
+ async def _inner(*args, **kw):
+ self._patch_dict()
+ try:
+ return await f(*args, **kw)
+ finally:
+ self._unpatch_dict()
+
+ return _inner
+
+
def decorate_class(self, klass):
for attr in dir(klass):
attr_value = getattr(klass, attr)
@@ -1907,7 +2076,7 @@ if IS_PYPY:
magic_methods = magic_methods.replace('sizeof ', '')
numerics = (
- "add sub mul matmul div floordiv mod lshift rshift and xor or pow truediv"
+ "add sub mul matmul truediv floordiv mod lshift rshift and xor or pow"
)
inplace = ' '.join('i%s' % n for n in numerics.split())
right = ' '.join('r%s' % n for n in numerics.split())
@@ -1919,7 +2088,7 @@ right = ' '.join('r%s' % n for n in numerics.split())
_non_defaults = {
'__get__', '__set__', '__delete__', '__reversed__', '__missing__',
'__reduce__', '__reduce_ex__', '__getinitargs__', '__getnewargs__',
- '__getstate__', '__setstate__', '__getformat__', '__setformat__',
+ '__getstate__', '__setstate__', '__getformat__',
'__repr__', '__dir__', '__subclasses__', '__format__',
'__getnewargs_ex__',
}
@@ -2055,8 +2224,6 @@ class MagicMixin(Base):
if getattr(self, "_mock_methods", None) is not None:
these_magics = orig_magics.intersection(self._mock_methods)
-
- remove_magics = set()
remove_magics = orig_magics - these_magics
for entry in remove_magics:
@@ -2086,10 +2253,7 @@ class NonCallableMagicMock(MagicMixin, NonCallableMock):
class AsyncMagicMixin(MagicMixin):
- def __init__(self, *args, **kw):
- self._mock_set_magics() # make magic work for kwargs in init
- _safe_super(AsyncMagicMixin, self).__init__(*args, **kw)
- self._mock_set_magics() # fix magic broken by upper level init
+ pass
class MagicMock(MagicMixin, Mock):
@@ -2112,6 +2276,17 @@ class MagicMock(MagicMixin, Mock):
self._mock_add_spec(spec, spec_set)
self._mock_set_magics()
+ def reset_mock(self, *args, return_value: bool = False, **kwargs):
+ if (
+ return_value
+ and self._mock_name
+ and _is_magic(self._mock_name)
+ ):
+ # Don't reset return values for magic methods,
+ # otherwise `m.__str__` will start
+ # to return `MagicMock` instances, instead of `str` instances.
+ return_value = False
+ super().reset_mock(*args, return_value=return_value, **kwargs)
class MagicProxy(Base):
@@ -2132,6 +2307,13 @@ class MagicProxy(Base):
return self.create_mock()
+try:
+ _CODE_SIG = inspect.signature(partial(CodeType.__init__, None))
+ _CODE_ATTRS = dir(CodeType)
+except ValueError: # pragma: no cover - backport is only tested against builds with docstrings
+ _CODE_SIG = None
+
+
class AsyncMockMixin(Base):
await_count = _delegating_property('await_count')
await_args = _delegating_property('await_args')
@@ -2149,9 +2331,30 @@ class AsyncMockMixin(Base):
self.__dict__['_mock_await_count'] = 0
self.__dict__['_mock_await_args'] = None
self.__dict__['_mock_await_args_list'] = _CallList()
- code_mock = NonCallableMock(spec_set=CodeType)
- code_mock.co_flags = inspect.CO_COROUTINE
+ if _CODE_SIG:
+ code_mock = NonCallableMock(spec_set=_CODE_ATTRS)
+ code_mock.__dict__["_spec_class"] = CodeType
+ code_mock.__dict__["_spec_signature"] = _CODE_SIG
+ else: # pragma: no cover - backport is only tested against builds with docstrings
+ code_mock = NonCallableMock(spec_set=CodeType)
+ code_mock.co_flags = (
+ inspect.CO_COROUTINE
+ + inspect.CO_VARARGS
+ + inspect.CO_VARKEYWORDS
+ )
+ code_mock.co_argcount = 0
+ code_mock.co_varnames = ('args', 'kwargs')
+ try:
+ code_mock.co_posonlyargcount = 0
+ except AttributeError:
+ # Python 3.7 and earlier.
+ pass
+ code_mock.co_kwonlyargcount = 0
self.__dict__['__code__'] = code_mock
+ self.__dict__['__name__'] = 'AsyncMock'
+ self.__dict__['__defaults__'] = tuple()
+ self.__dict__['__kwdefaults__'] = {}
+ self.__dict__['__annotations__'] = None
async def _execute_mock_call(_mock_self, *args, **kwargs):
self = _mock_self
@@ -2171,7 +2374,7 @@ class AsyncMockMixin(Base):
try:
result = next(effect)
except StopIteration:
- # It is impossible to propogate a StopIteration
+ # It is impossible to propagate a StopIteration
# through coroutines because of PEP 479
raise StopAsyncIteration
if _is_exception(result):
@@ -2589,7 +2792,7 @@ call = _Call(from_kall=False)
def create_autospec(spec, spec_set=False, instance=False, _parent=None,
- _name=None, **kwargs):
+ _name=None, *, unsafe=False, **kwargs):
"""Create a mock object using another object as a spec. Attributes on the
mock will use the corresponding attribute on the `spec` object as their
spec.
@@ -2605,6 +2808,10 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
spec for an instance object by passing `instance=True`. The returned mock
will only be callable if instances of the mock are callable.
+ `create_autospec` will raise a `RuntimeError` if passed some common
+ misspellings of the arguments autospec and spec_set. Pass the argument
+ `unsafe` with the value True to disable that check.
+
`create_autospec` also takes arbitrary keyword arguments that are passed to
the constructor of the created mock."""
if _is_list(spec):
@@ -2613,8 +2820,19 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
spec = type(spec)
is_type = isinstance(spec, type)
+ if _is_instance_mock(spec):
+ raise InvalidSpecError(f'Cannot autospec a Mock object. '
+ f'[object={spec!r}]')
is_async_func = _is_async_func(spec)
- _kwargs = {'spec': spec}
+
+ entries = [(entry, _missing) for entry in dir(spec)]
+ if is_type and instance and HAS_DATACLASSES and is_dataclass(spec):
+ dataclass_fields = fields(spec)
+ entries.extend((f.name, f.type) for f in dataclass_fields)
+ _kwargs = {'spec': [f.name for f in dataclass_fields]}
+ else:
+ _kwargs = {'spec': spec}
+
if spec_set:
_kwargs = {'spec_set': spec}
elif spec is None:
@@ -2622,6 +2840,14 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
_kwargs = {}
if _kwargs and instance:
_kwargs['_spec_as_instance'] = True
+ if not unsafe:
+ _check_spec_arg_typos(kwargs)
+
+ _name = kwargs.pop('name', _name)
+ _new_name = _name
+ if _parent is None:
+ # for a top level object no _new_name should be set
+ _new_name = ''
_kwargs.update(kwargs)
@@ -2640,33 +2866,30 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
elif is_type and instance and not _instance_callable(spec):
Klass = NonCallableMagicMock
- _name = _kwargs.pop('name', _name)
-
- _new_name = _name
- if _parent is None:
- # for a top level object no _new_name should be set
- _new_name = ''
-
mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
name=_name, **_kwargs)
if isinstance(spec, FunctionTypes):
# should only happen at the top level because we don't
# recurse for functions
- mock = _set_signature(mock, spec)
if is_async_func:
- _setup_async_mock(mock)
+ mock = _set_async_signature(mock, spec)
+ else:
+ mock = _set_signature(mock, spec)
else:
_check_signature(spec, mock, is_type, instance)
if _parent is not None and not instance:
_parent._mock_children[_name] = mock
+ # Pop wraps from kwargs because it must not be passed to configure_mock.
+ wrapped = kwargs.pop('wraps', None)
if is_type and not instance and 'return_value' not in kwargs:
mock.return_value = create_autospec(spec, spec_set, instance=True,
- _name='()', _parent=mock)
+ _name='()', _parent=mock,
+ wraps=wrapped)
- for entry in dir(spec):
+ for entry, original in entries:
if _is_magic(entry):
# MagicMock already does the useful magic methods for us
continue
@@ -2680,14 +2903,18 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
# AttributeError on being fetched?
# we could be resilient against it, or catch and propagate the
# exception when the attribute is fetched from the mock
- try:
- original = getattr(spec, entry)
- except AttributeError:
- continue
+ if original is _missing:
+ try:
+ original = getattr(spec, entry)
+ except AttributeError:
+ continue
- kwargs = {'spec': original}
+ child_kwargs = {'spec': original}
+ # Wrap child attributes also.
+ if wrapped and hasattr(wrapped, entry):
+ child_kwargs.update(wraps=original)
if spec_set:
- kwargs = {'spec_set': original}
+ child_kwargs = {'spec_set': original}
if not isinstance(original, FunctionTypes):
new = _SpecState(original, spec_set, mock, entry, instance)
@@ -2698,15 +2925,15 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
parent = mock.mock
skipfirst = _must_skip(spec, entry, is_type)
- kwargs['_eat_self'] = skipfirst
+ child_kwargs['_eat_self'] = skipfirst
if iscoroutinefunction(original):
child_klass = AsyncMock
else:
child_klass = MagicMock
new = child_klass(parent=parent, name=entry, _new_name=entry,
- _new_parent=parent,
- **kwargs)
+ _new_parent=parent, **child_kwargs)
mock._mock_children[entry] = new
+ new.return_value = child_klass()
_check_signature(original, new, skipfirst=skipfirst)
# so functions created with _set_signature become instance attributes,
@@ -2715,6 +2942,11 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
# setting as an instance attribute?
if isinstance(new, FunctionTypes):
setattr(mock, entry, new)
+ # kwargs are passed with respect to the parent mock so, they are not used
+ # for creating return_value of the parent mock. So, this condition
+ # should be true only for the parent mock if kwargs are given.
+ if _is_instance_mock(mock) and kwargs:
+ mock.configure_mock(**kwargs)
return mock
@@ -2768,6 +3000,7 @@ FunctionTypes = (
file_spec = None
+open_spec = None
def _to_stream(read_data):
@@ -2819,13 +3052,20 @@ def mock_open(mock=None, read_data=''):
return handle.readline.return_value
return next(_state[0])
+ def _exit_side_effect(exctype, excinst, exctb):
+ handle.close()
+
global file_spec
if file_spec is None:
import _io
file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
+ global open_spec
+ if open_spec is None:
+ import _io
+ open_spec = list(set(dir(_io.open)))
if mock is None:
- mock = MagicMock(name='open', spec=open)
+ mock = MagicMock(name='open', spec=open_spec)
handle = MagicMock(spec=file_spec)
handle.__enter__.return_value = handle
@@ -2841,6 +3081,7 @@ def mock_open(mock=None, read_data=''):
handle.readlines.side_effect = _readlines_side_effect
handle.__iter__.side_effect = _iter_side_effect
handle.__next__.side_effect = _next_side_effect
+ handle.__exit__.side_effect = _exit_side_effect
def reset_data(*args, **kwargs):
_state[0] = _to_stream(read_data)
@@ -2873,6 +3114,96 @@ class PropertyMock(Mock):
self(val)
+_timeout_unset = sentinel.TIMEOUT_UNSET
+
+class ThreadingMixin(Base):
+
+ DEFAULT_TIMEOUT = None
+
+ def _get_child_mock(self, **kw):
+ if isinstance(kw.get("parent"), ThreadingMixin):
+ kw["timeout"] = kw["parent"]._mock_wait_timeout
+ elif isinstance(kw.get("_new_parent"), ThreadingMixin):
+ kw["timeout"] = kw["_new_parent"]._mock_wait_timeout
+ return super()._get_child_mock(**kw)
+
+ def __init__(self, *args, timeout=_timeout_unset, **kwargs):
+ super().__init__(*args, **kwargs)
+ if timeout is _timeout_unset:
+ timeout = self.DEFAULT_TIMEOUT
+ self.__dict__["_mock_event"] = threading.Event() # Event for any call
+ self.__dict__["_mock_calls_events"] = [] # Events for each of the calls
+ self.__dict__["_mock_calls_events_lock"] = threading.Lock()
+ self.__dict__["_mock_wait_timeout"] = timeout
+
+ def reset_mock(self, *args, **kwargs):
+ """
+ See :func:`.Mock.reset_mock()`
+ """
+ super().reset_mock(*args, **kwargs)
+ self.__dict__["_mock_event"] = threading.Event()
+ self.__dict__["_mock_calls_events"] = []
+
+ def __get_event(self, expected_args, expected_kwargs):
+ with self._mock_calls_events_lock:
+ for args, kwargs, event in self._mock_calls_events:
+ if (args, kwargs) == (expected_args, expected_kwargs):
+ return event
+ new_event = threading.Event()
+ self._mock_calls_events.append((expected_args, expected_kwargs, new_event))
+ return new_event
+
+ def _mock_call(self, *args, **kwargs):
+ ret_value = super()._mock_call(*args, **kwargs)
+
+ call_event = self.__get_event(args, kwargs)
+ call_event.set()
+
+ self._mock_event.set()
+
+ return ret_value
+
+ def wait_until_called(self, *, timeout=_timeout_unset):
+ """Wait until the mock object is called.
+
+ `timeout` - time to wait for in seconds, waits forever otherwise.
+ Defaults to the constructor provided timeout.
+ Use None to block undefinetively.
+ """
+ if timeout is _timeout_unset:
+ timeout = self._mock_wait_timeout
+ if not self._mock_event.wait(timeout=timeout):
+ msg = (f"{self._mock_name or 'mock'} was not called before"
+ f" timeout({timeout}).")
+ raise AssertionError(msg)
+
+ def wait_until_any_call_with(self, *args, **kwargs):
+ """Wait until the mock object is called with given args.
+
+ Waits for the timeout in seconds provided in the constructor.
+ """
+ event = self.__get_event(args, kwargs)
+ if not event.wait(timeout=self._mock_wait_timeout):
+ expected_string = self._format_mock_call_signature(args, kwargs)
+ raise AssertionError(f'{expected_string} call not found')
+
+
+class ThreadingMock(ThreadingMixin, MagicMixin, Mock):
+ """
+ A mock that can be used to wait until on calls happening
+ in a different thread.
+
+ The constructor can take a `timeout` argument which
+ controls the timeout in seconds for all `wait` calls of the mock.
+
+ You can change the default timeout of all instances via the
+ `ThreadingMock.DEFAULT_TIMEOUT` attribute.
+
+ If no timeout is set, it will block undefinetively.
+ """
+ pass
+
+
def seal(mock):
"""Disable the automatic generation of child mocks.
@@ -2891,6 +3222,8 @@ def seal(mock):
continue
if not isinstance(m, NonCallableMock):
continue
+ if isinstance(m._mock_children.get(attr), _SpecState):
+ continue
if m._mock_new_parent is mock:
seal(m)
diff --git a/contrib/python/mock/py3/ya.make b/contrib/python/mock/py3/ya.make
index 841a1f2aff0..0c4eedacf69 100644
--- a/contrib/python/mock/py3/ya.make
+++ b/contrib/python/mock/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.0.3)
+VERSION(5.2.0)
LICENSE(BSD-3-Clause)