diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-04-18 12:39:32 +0300 |
---|---|---|
committer | shadchin <shadchin@yandex-team.ru> | 2022-04-18 12:39:32 +0300 |
commit | d4be68e361f4258cf0848fc70018dfe37a2acc24 (patch) | |
tree | 153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Lib/unittest/mock.py | |
parent | 260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff) | |
download | ydb-d4be68e361f4258cf0848fc70018dfe37a2acc24.tar.gz |
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Lib/unittest/mock.py')
-rw-r--r-- | contrib/tools/python3/src/Lib/unittest/mock.py | 84 |
1 files changed, 71 insertions, 13 deletions
diff --git a/contrib/tools/python3/src/Lib/unittest/mock.py b/contrib/tools/python3/src/Lib/unittest/mock.py index 3f5442ed80..7152f86ed9 100644 --- a/contrib/tools/python3/src/Lib/unittest/mock.py +++ b/contrib/tools/python3/src/Lib/unittest/mock.py @@ -36,6 +36,10 @@ from unittest.util import safe_repr from functools import wraps, partial +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 @@ -631,9 +635,10 @@ class NonCallableMock(Base): elif _is_magic(name): raise AttributeError(name) if not self._mock_unsafe: - if name.startswith(('assert', 'assret')): - raise AttributeError("Attributes cannot start with 'assert' " - "or 'assret'") + if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')): + 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: @@ -652,10 +657,17 @@ class NonCallableMock(Base): self._mock_children[name] = result elif isinstance(result, _SpecState): - result = create_autospec( - result.spec, result.spec_set, result.instance, - result.parent, result.name - ) + 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 @@ -1240,6 +1252,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 @@ -1247,7 +1270,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: @@ -1258,6 +1281,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 @@ -1485,6 +1518,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: @@ -1567,7 +1612,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 @@ -1589,7 +1634,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 ) @@ -1644,7 +1689,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 @@ -1706,6 +1751,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. @@ -1716,7 +1765,7 @@ 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 ) @@ -2566,7 +2615,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. @@ -2582,6 +2631,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): @@ -2590,6 +2643,9 @@ 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} if spec_set: @@ -2599,6 +2655,8 @@ 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) _kwargs.update(kwargs) |