diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-04-05 15:19:56 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-04-05 15:32:02 +0300 |
commit | aec95948f1137dd6c659d7160160b05cfa11e0c7 (patch) | |
tree | c22e5e1d22f7954d33208806d2cdf5ac65d8ff38 | |
parent | 8c029b63ffc0e0f522561452134e7f01d5db1844 (diff) | |
download | ydb-aec95948f1137dd6c659d7160160b05cfa11e0c7.tar.gz |
Intermediate changes
-rw-r--r-- | contrib/python/pytest-mock/py3/.dist-info/METADATA | 16 | ||||
-rw-r--r-- | contrib/python/pytest-mock/py3/pytest_mock/__init__.py | 8 | ||||
-rw-r--r-- | contrib/python/pytest-mock/py3/pytest_mock/_version.py | 4 | ||||
-rw-r--r-- | contrib/python/pytest-mock/py3/pytest_mock/plugin.py | 165 | ||||
-rw-r--r-- | contrib/python/pytest-mock/py3/ya.make | 2 | ||||
-rw-r--r-- | yt/python/yt/type_info/__init__.py | 2 | ||||
-rw-r--r-- | yt/python/yt/type_info/type_base.py | 14 | ||||
-rw-r--r-- | yt/python/yt/type_info/typing.py | 40 |
8 files changed, 154 insertions, 97 deletions
diff --git a/contrib/python/pytest-mock/py3/.dist-info/METADATA b/contrib/python/pytest-mock/py3/.dist-info/METADATA index 2d35e95446..1ffa498c29 100644 --- a/contrib/python/pytest-mock/py3/.dist-info/METADATA +++ b/contrib/python/pytest-mock/py3/.dist-info/METADATA @@ -1,38 +1,36 @@ Metadata-Version: 2.1 Name: pytest-mock -Version: 3.12.0 +Version: 3.14.0 Summary: Thin-wrapper around the mock package for easier use with pytest -Home-page: https://github.com/pytest-dev/pytest-mock/ -Author: Bruno Oliveira -Author-email: nicoddemus@gmail.com +Author-email: Bruno Oliveira <nicoddemus@gmail.com> License: MIT +Project-URL: Homepage, https://github.com/pytest-dev/pytest-mock/ Project-URL: Documentation, https://pytest-mock.readthedocs.io/en/latest/ Project-URL: Changelog, https://pytest-mock.readthedocs.io/en/latest/changelog.html Project-URL: Source, https://github.com/pytest-dev/pytest-mock/ Project-URL: Tracker, https://github.com/pytest-dev/pytest-mock/issues -Keywords: pytest mock -Platform: any +Keywords: pytest,mock Classifier: Development Status :: 5 - Production/Stable Classifier: Framework :: Pytest Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only 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 :: Only Classifier: Topic :: Software Development :: Testing Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE -Requires-Dist: pytest >=5.0 +Requires-Dist: pytest >=6.2.5 Provides-Extra: dev Requires-Dist: pre-commit ; extra == 'dev' -Requires-Dist: tox ; extra == 'dev' Requires-Dist: pytest-asyncio ; extra == 'dev' +Requires-Dist: tox ; extra == 'dev' =========== pytest-mock diff --git a/contrib/python/pytest-mock/py3/pytest_mock/__init__.py b/contrib/python/pytest-mock/py3/pytest_mock/__init__.py index 0afa47c0c1..75fd27afde 100644 --- a/contrib/python/pytest-mock/py3/pytest_mock/__init__.py +++ b/contrib/python/pytest-mock/py3/pytest_mock/__init__.py @@ -1,18 +1,22 @@ +from pytest_mock.plugin import AsyncMockType +from pytest_mock.plugin import MockerFixture +from pytest_mock.plugin import MockType +from pytest_mock.plugin import PytestMockWarning from pytest_mock.plugin import class_mocker from pytest_mock.plugin import mocker -from pytest_mock.plugin import MockerFixture from pytest_mock.plugin import module_mocker from pytest_mock.plugin import package_mocker from pytest_mock.plugin import pytest_addoption from pytest_mock.plugin import pytest_configure -from pytest_mock.plugin import PytestMockWarning from pytest_mock.plugin import session_mocker MockFixture = MockerFixture # backward-compatibility only (#204) __all__ = [ + "AsyncMockType", "MockerFixture", "MockFixture", + "MockType", "PytestMockWarning", "pytest_addoption", "pytest_configure", diff --git a/contrib/python/pytest-mock/py3/pytest_mock/_version.py b/contrib/python/pytest-mock/py3/pytest_mock/_version.py index 9e1a89958d..431bd6d819 100644 --- a/contrib/python/pytest-mock/py3/pytest_mock/_version.py +++ b/contrib/python/pytest-mock/py3/pytest_mock/_version.py @@ -12,5 +12,5 @@ __version__: str __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '3.12.0' -__version_tuple__ = version_tuple = (3, 12, 0) +__version__ = version = '3.14.0' +__version_tuple__ = version_tuple = (3, 14, 0) diff --git a/contrib/python/pytest-mock/py3/pytest_mock/plugin.py b/contrib/python/pytest-mock/py3/pytest_mock/plugin.py index 673f98bffb..1e0a0b2025 100644 --- a/contrib/python/pytest-mock/py3/pytest_mock/plugin.py +++ b/contrib/python/pytest-mock/py3/pytest_mock/plugin.py @@ -2,23 +2,25 @@ import asyncio import builtins import functools import inspect -import sys import unittest.mock import warnings +from dataclasses import dataclass +from dataclasses import field from typing import Any from typing import Callable -from typing import cast from typing import Dict from typing import Generator from typing import Iterable +from typing import Iterator from typing import List from typing import Mapping from typing import Optional -from typing import overload from typing import Tuple from typing import Type from typing import TypeVar from typing import Union +from typing import cast +from typing import overload import pytest @@ -27,22 +29,58 @@ from ._util import parse_ini_boolean _T = TypeVar("_T") -if sys.version_info >= (3, 8): - AsyncMockType = unittest.mock.AsyncMock - MockType = Union[ - unittest.mock.MagicMock, - unittest.mock.AsyncMock, - unittest.mock.NonCallableMagicMock, - ] -else: - AsyncMockType = Any - MockType = Union[unittest.mock.MagicMock, unittest.mock.NonCallableMagicMock] +AsyncMockType = unittest.mock.AsyncMock +MockType = Union[ + unittest.mock.MagicMock, + unittest.mock.AsyncMock, + unittest.mock.NonCallableMagicMock, +] class PytestMockWarning(UserWarning): """Base class for all warnings emitted by pytest-mock.""" +@dataclass +class MockCacheItem: + mock: MockType + patch: Optional[Any] = None + + +@dataclass +class MockCache: + """ + Cache MagicMock and Patcher instances so we can undo them later. + """ + + cache: List[MockCacheItem] = field(default_factory=list) + + def _find(self, mock: MockType) -> MockCacheItem: + for mock_item in self.cache: + if mock_item.mock is mock: + return mock_item + raise ValueError("This mock object is not registered") + + def add(self, mock: MockType, **kwargs: Any) -> MockCacheItem: + self.cache.append(MockCacheItem(mock=mock, **kwargs)) + return self.cache[-1] + + def remove(self, mock: MockType) -> None: + mock_item = self._find(mock) + if mock_item.patch: + mock_item.patch.stop() + self.cache.remove(mock_item) + + def clear(self) -> None: + for mock_item in reversed(self.cache): + if mock_item.patch is not None: + mock_item.patch.stop() + self.cache.clear() + + def __iter__(self) -> Iterator[MockCacheItem]: + return iter(self.cache) + + class MockerFixture: """ Fixture that provides the same interface to functions in the mock module, @@ -50,11 +88,9 @@ class MockerFixture: """ def __init__(self, config: Any) -> None: - self._patches_and_mocks: List[Tuple[Any, unittest.mock.MagicMock]] = [] + self._mock_cache: MockCache = MockCache() self.mock_module = mock_module = get_mock_module(config) - self.patch = self._Patcher( - self._patches_and_mocks, mock_module - ) # type: MockerFixture._Patcher + self.patch = self._Patcher(self._mock_cache, mock_module) # type: MockerFixture._Patcher # aliases for convenience self.Mock = mock_module.Mock self.MagicMock = mock_module.MagicMock @@ -77,7 +113,7 @@ class MockerFixture: m: MockType = self.mock_module.create_autospec( spec, spec_set, instance, **kwargs ) - self._patches_and_mocks.append((None, m)) + self._mock_cache.add(m) return m def resetall( @@ -95,37 +131,33 @@ class MockerFixture: else: supports_reset_mock_with_args = (self.Mock,) - for p, m in self._patches_and_mocks: + for mock_item in self._mock_cache: # See issue #237. - if not hasattr(m, "reset_mock"): + if not hasattr(mock_item.mock, "reset_mock"): continue - if isinstance(m, supports_reset_mock_with_args): - m.reset_mock(return_value=return_value, side_effect=side_effect) + # NOTE: The mock may be a dictionary + if hasattr(mock_item.mock, "spy_return_list"): + mock_item.mock.spy_return_list = [] + if isinstance(mock_item.mock, supports_reset_mock_with_args): + mock_item.mock.reset_mock( + return_value=return_value, side_effect=side_effect + ) else: - m.reset_mock() + mock_item.mock.reset_mock() def stopall(self) -> None: """ Stop all patchers started by this fixture. Can be safely called multiple times. """ - for p, m in reversed(self._patches_and_mocks): - if p is not None: - p.stop() - self._patches_and_mocks.clear() + self._mock_cache.clear() def stop(self, mock: unittest.mock.MagicMock) -> None: """ Stops a previous patch or spy call by passing the ``MagicMock`` object returned by it. """ - for index, (p, m) in enumerate(self._patches_and_mocks): - if mock is m: - p.stop() - del self._patches_and_mocks[index] - break - else: - raise ValueError("This mock object is not registered") + self._mock_cache.remove(mock) def spy(self, obj: object, name: str) -> MockType: """ @@ -137,14 +169,6 @@ class MockerFixture: :return: Spy object. """ method = getattr(obj, name) - if inspect.isclass(obj) and isinstance( - inspect.getattr_static(obj, name), (classmethod, staticmethod) - ): - # Can't use autospec classmethod or staticmethod objects before 3.7 - # see: https://bugs.python.org/issue23078 - autospec = False - else: - autospec = inspect.ismethod(method) or inspect.isfunction(method) def wrapper(*args, **kwargs): spy_obj.spy_return = None @@ -156,6 +180,7 @@ class MockerFixture: raise else: spy_obj.spy_return = r + spy_obj.spy_return_list.append(r) return r async def async_wrapper(*args, **kwargs): @@ -168,6 +193,7 @@ class MockerFixture: raise else: spy_obj.spy_return = r + spy_obj.spy_return_list.append(r) return r if asyncio.iscoroutinefunction(method): @@ -175,8 +201,11 @@ class MockerFixture: else: wrapped = functools.update_wrapper(wrapper, method) + autospec = inspect.ismethod(method) or inspect.isfunction(method) + spy_obj = self.patch.object(obj, name, side_effect=wrapped, autospec=autospec) spy_obj.spy_return = None + spy_obj.spy_return_list = [] spy_obj.spy_exception = None return spy_obj @@ -214,8 +243,8 @@ class MockerFixture: DEFAULT = object() - def __init__(self, patches_and_mocks, mock_module): - self.__patches_and_mocks = patches_and_mocks + def __init__(self, mock_cache, mock_module): + self.__mock_cache = mock_cache self.mock_module = mock_module def _start_patch( @@ -227,22 +256,18 @@ class MockerFixture: """ p = mock_func(*args, **kwargs) mocked: MockType = p.start() - self.__patches_and_mocks.append((p, mocked)) + self.__mock_cache.add(mock=mocked, patch=p) if hasattr(mocked, "reset_mock"): # check if `mocked` is actually a mock object, as depending on autospec or target # parameters `mocked` can be anything if hasattr(mocked, "__enter__") and warn_on_mock_enter: - if sys.version_info >= (3, 8): - depth = 5 - else: - depth = 4 mocked.__enter__.side_effect = lambda: warnings.warn( "Mocks returned by pytest-mock do not need to be used as context managers. " "The mocker fixture automatically undoes mocking at the end of a test. " "This warning can be ignored if it was triggered by mocking a context manager. " "https://pytest-mock.readthedocs.io/en/latest/remarks.html#usage-as-context-manager", PytestMockWarning, - stacklevel=depth, + stacklevel=5, ) return mocked @@ -256,7 +281,7 @@ class MockerFixture: spec_set: Optional[object] = None, autospec: Optional[object] = None, new_callable: object = None, - **kwargs: Any + **kwargs: Any, ) -> MockType: """API to mock.patch.object""" if new is self.DEFAULT: @@ -272,7 +297,7 @@ class MockerFixture: spec_set=spec_set, autospec=autospec, new_callable=new_callable, - **kwargs + **kwargs, ) def context_manager( @@ -285,7 +310,7 @@ class MockerFixture: spec_set: Optional[builtins.object] = None, autospec: Optional[builtins.object] = None, new_callable: builtins.object = None, - **kwargs: Any + **kwargs: Any, ) -> MockType: """This is equivalent to mock.patch.object except that the returned mock does not issue a warning when used as a context manager.""" @@ -302,7 +327,7 @@ class MockerFixture: spec_set=spec_set, autospec=autospec, new_callable=new_callable, - **kwargs + **kwargs, ) def multiple( @@ -313,7 +338,7 @@ class MockerFixture: spec_set: Optional[builtins.object] = None, autospec: Optional[builtins.object] = None, new_callable: Optional[builtins.object] = None, - **kwargs: Any + **kwargs: Any, ) -> Dict[str, MockType]: """API to mock.patch.multiple""" return self._start_patch( @@ -325,7 +350,7 @@ class MockerFixture: spec_set=spec_set, autospec=autospec, new_callable=new_callable, - **kwargs + **kwargs, ) def dict( @@ -333,7 +358,7 @@ class MockerFixture: in_dict: Union[Mapping[Any, Any], str], values: Union[Mapping[Any, Any], Iterable[Tuple[Any, Any]]] = (), clear: bool = False, - **kwargs: Any + **kwargs: Any, ) -> Any: """API to mock.patch.dict""" return self._start_patch( @@ -342,7 +367,7 @@ class MockerFixture: in_dict, values=values, clear=clear, - **kwargs + **kwargs, ) @overload @@ -355,9 +380,8 @@ class MockerFixture: spec_set: Optional[builtins.object] = ..., autospec: Optional[builtins.object] = ..., new_callable: None = ..., - **kwargs: Any - ) -> MockType: - ... + **kwargs: Any, + ) -> MockType: ... @overload def __call__( @@ -369,9 +393,8 @@ class MockerFixture: spec_set: Optional[builtins.object] = ..., autospec: Optional[builtins.object] = ..., new_callable: None = ..., - **kwargs: Any - ) -> _T: - ... + **kwargs: Any, + ) -> _T: ... @overload def __call__( @@ -383,9 +406,8 @@ class MockerFixture: spec_set: Optional[builtins.object], autospec: Optional[builtins.object], new_callable: Callable[[], _T], - **kwargs: Any - ) -> _T: - ... + **kwargs: Any, + ) -> _T: ... @overload def __call__( @@ -398,9 +420,8 @@ class MockerFixture: autospec: Optional[builtins.object] = ..., *, new_callable: Callable[[], _T], - **kwargs: Any - ) -> _T: - ... + **kwargs: Any, + ) -> _T: ... def __call__( self, @@ -411,7 +432,7 @@ class MockerFixture: spec_set: Optional[builtins.object] = None, autospec: Optional[builtins.object] = None, new_callable: Optional[Callable[[], Any]] = None, - **kwargs: Any + **kwargs: Any, ) -> Any: """API to mock.patch""" if new is self.DEFAULT: @@ -426,7 +447,7 @@ class MockerFixture: spec_set=spec_set, autospec=autospec, new_callable=new_callable, - **kwargs + **kwargs, ) diff --git a/contrib/python/pytest-mock/py3/ya.make b/contrib/python/pytest-mock/py3/ya.make index 40f864e45d..7b9bcc9b14 100644 --- a/contrib/python/pytest-mock/py3/ya.make +++ b/contrib/python/pytest-mock/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(3.12.0) +VERSION(3.14.0) LICENSE(MIT) diff --git a/yt/python/yt/type_info/__init__.py b/yt/python/yt/type_info/__init__.py index aa7268843c..33813771da 100644 --- a/yt/python/yt/type_info/__init__.py +++ b/yt/python/yt/type_info/__init__.py @@ -7,5 +7,5 @@ from .typing import ( # noqa Double, String, Utf8, Yson, Json, Uuid, Date, Datetime, Timestamp, Interval, TzDate, TzDatetime, TzTimestamp, Void, Null, Optional, List, Tuple, Dict, Struct, Variant, Tagged, Decimal, EmptyTuple, EmptyStruct, - serialize_yson, deserialize_yson, + serialize_yson, deserialize_yson, deserialize_yson_v1, ) diff --git a/yt/python/yt/type_info/type_base.py b/yt/python/yt/type_info/type_base.py index 57b5452e29..11b75fd2a1 100644 --- a/yt/python/yt/type_info/type_base.py +++ b/yt/python/yt/type_info/type_base.py @@ -82,6 +82,9 @@ class Primitive(Type): def to_yson_type(self): return self.yt_type_name + def to_yson_type_v1(self): + return self.yt_type_name_v1 + class Generic(six.with_metaclass(ABCMeta)): def __init__(self, name, yt_type_name=None): @@ -98,7 +101,7 @@ class Generic(six.with_metaclass(ABCMeta)): pass -def make_primitive_type(name, yt_type_name=None): +def make_primitive_type(name, yt_type_name=None, yt_type_name_v1=None): assert _is_utf8(name), "Name of primitive type must be UTF-8, got {}".format(_with_type(name)) assert yt_type_name is None or _is_utf8(yt_type_name), \ "YT type name of primitive type must be UTF-8, got {}".format(_with_type(name)) @@ -106,4 +109,11 @@ def make_primitive_type(name, yt_type_name=None): if yt_type_name is None: yt_type_name = name.lower() - return Primitive({"name": name, "yt_type_name": yt_type_name}) + if yt_type_name_v1 is None: + yt_type_name_v1 = name.lower() + + return Primitive({ + "name": name, + "yt_type_name": yt_type_name, + "yt_type_name_v1": yt_type_name_v1, + }) diff --git a/yt/python/yt/type_info/typing.py b/yt/python/yt/type_info/typing.py index 07cd5c189c..0d9abc505f 100644 --- a/yt/python/yt/type_info/typing.py +++ b/yt/python/yt/type_info/typing.py @@ -341,7 +341,9 @@ class _GenericDecimal(type_base.Generic): return self.__getitem__((type_["precision"], type_["scale"],)) -Bool = type_base.make_primitive_type("Bool") +Bool = type_base.make_primitive_type("Bool", yt_type_name_v1="boolean") +Yson = type_base.make_primitive_type("Yson", yt_type_name_v1="any") + Int8 = type_base.make_primitive_type("Int8") Uint8 = type_base.make_primitive_type("Uint8") Int16 = type_base.make_primitive_type("Int16") @@ -354,7 +356,6 @@ Float = type_base.make_primitive_type("Float") Double = type_base.make_primitive_type("Double") String = type_base.make_primitive_type("String") Utf8 = type_base.make_primitive_type("Utf8") -Yson = type_base.make_primitive_type("Yson") Json = type_base.make_primitive_type("Json") Uuid = type_base.make_primitive_type("Uuid") Date = type_base.make_primitive_type("Date") @@ -381,7 +382,8 @@ Decimal = _GenericDecimal("Decimal") EmptyTuple = Tuple.__getitem__(tuple()) EmptyStruct = Struct.__getitem__(tuple()) -PRIMITIVES = {type_.yt_type_name: type_ for type_ in locals().values() if isinstance(type_, type_base.Primitive)} +PRIMITIVES_V1 = {type_.yt_type_name_v1: type_ for type_ in locals().values() if isinstance(type_, type_base.Primitive)} +PRIMITIVES_V3 = {type_.yt_type_name: type_ for type_ in locals().values() if isinstance(type_, type_base.Primitive)} GENERICS = {type_.yt_type_name: type_ for type_ in locals().values() if isinstance(type_, type_base.Generic)} @@ -396,8 +398,8 @@ def _validate_contains(dict, key): def _parse_type(type_description): if isinstance(type_description, six.string_types): - _validate(type_description in PRIMITIVES, "unknown type \"{}\"".format(type_description)) - return PRIMITIVES[type_description] + _validate(type_description in PRIMITIVES_V3, "unknown type \"{}\"".format(type_description)) + return PRIMITIVES_V3[type_description] _validate(isinstance(type_description, dict), "type must be either a string or a map, got {}".format(type_base._with_type(type_description))) @@ -407,14 +409,23 @@ def _parse_type(type_description): type_name = type_description["type_name"] _validate(isinstance(type_name, six.string_types), "\"type_name\" must contain a string") - _validate(type_name in PRIMITIVES or type_name in GENERICS, "unknown type \"{}\"".format(type_name)) + _validate(type_name in PRIMITIVES_V3 or type_name in GENERICS, "unknown type \"{}\"".format(type_name)) - if type_name in PRIMITIVES: - return PRIMITIVES[type_name] + if type_name in PRIMITIVES_V3: + return PRIMITIVES_V3[type_name] return GENERICS[type_name].from_dict(type_description) +def _parse_type_v1(type_description, required): + if required: + _validate(isinstance(type_description, six.string_types), "\"type_description\" must be a string for v1 type") + _validate(type_description in PRIMITIVES_V1, "unknown type \"{}\"".format(type_description)) + return PRIMITIVES_V1[type_description] + else: + return Optional[_parse_type_v1(type_description, True)] + + def _check_serialization_available(): if not _TI_SERIALIZATION_AVAILABLE: raise ImportError("Module `yt.yson` is required to use type_info serialization. " @@ -441,3 +452,16 @@ def deserialize_yson(yson): return _parse_type(type_description) except (ValueError, yt.yson.YsonError) as e: six.raise_from(ValueError("deserialization failed: {}".format(e)), e) + + +def deserialize_yson_v1(yson, required): + _check_serialization_available() + + if type(yson) is six.text_type and six.PY3: + yson = yson.encode("utf-8") + + try: + type_description = yt.yson.loads(yson) + return _parse_type_v1(type_description, required) + except (ValueError, yt.yson.YsonError) as e: + six.raise_from(ValueError("deserialization failed: {}".format(e)), e) |