diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-03-11 17:59:28 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-03-11 18:10:18 +0300 |
commit | a7a431a1c2a6704ba0cf2dbfe9e1c198945e8a9e (patch) | |
tree | 04c270807febe5f7da19a2510b450980f671f365 /contrib | |
parent | 7fa2009de5a7f9f102480fab66bdd624aa541755 (diff) | |
download | ydb-a7a431a1c2a6704ba0cf2dbfe9e1c198945e8a9e.tar.gz |
Intermediate changes
Diffstat (limited to 'contrib')
16 files changed, 332 insertions, 88 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 95fe6d3510..717ae021d6 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.98.11 +Version: 6.98.12 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py b/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py index 8917d5bd87..2854b48c29 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py @@ -122,7 +122,7 @@ from hypothesis.strategies._internal.flatmapped import FlatMapStrategy from hypothesis.strategies._internal.lazy import LazyStrategy, unwrap_strategies from hypothesis.strategies._internal.strategies import ( FilteredStrategy, - MappedSearchStrategy, + MappedStrategy, OneOfStrategy, SampledFromStrategy, ) @@ -627,7 +627,7 @@ def _imports_for_strategy(strategy): strategy = unwrap_strategies(strategy) # Get imports for s.map(f), s.filter(f), s.flatmap(f), including both s and f - if isinstance(strategy, MappedSearchStrategy): + if isinstance(strategy, MappedStrategy): imports |= _imports_for_strategy(strategy.mapped_strategy) imports |= _imports_for_object(strategy.pack) if isinstance(strategy, FilteredStrategy): diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py index 29d73f76be..4cfb1ca8d8 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py @@ -50,7 +50,7 @@ from hypothesis.strategies._internal.lazy import unwrap_strategies from hypothesis.strategies._internal.numbers import Real from hypothesis.strategies._internal.strategies import ( Ex, - MappedSearchStrategy, + MappedStrategy, T, check_strategy, ) @@ -516,7 +516,7 @@ def arrays( # If there's a redundant cast to the requested dtype, remove it. This unlocks # optimizations such as fast unique sampled_from, and saves some time directly too. unwrapped = unwrap_strategies(elements) - if isinstance(unwrapped, MappedSearchStrategy) and unwrapped.pack == dtype.type: + if isinstance(unwrapped, MappedStrategy) and unwrapped.pack == dtype.type: elements = unwrapped.mapped_strategy if isinstance(shape, int): shape = (shape,) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py index 4ba92b1da8..f352e9cb6d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py @@ -33,7 +33,10 @@ from typing import Any, Callable, Collection, Dict, NamedTuple, Optional, TypeVa from hypothesis.internal.compat import ceil, floor from hypothesis.internal.floats import next_down, next_up -from hypothesis.internal.reflection import extract_lambda_source +from hypothesis.internal.reflection import ( + extract_lambda_source, + get_pretty_function_description, +) Ex = TypeVar("Ex") Predicate = Callable[[Ex], bool] @@ -64,6 +67,10 @@ class ConstructivePredicate(NamedTuple): def unchanged(cls, predicate: Predicate) -> "ConstructivePredicate": return cls({}, predicate) + def __repr__(self) -> str: + fn = get_pretty_function_description(self.predicate) + return f"{self.__class__.__name__}(kwargs={self.kwargs!r}, predicate={fn})" + ARG = object() @@ -147,8 +154,8 @@ def merge_preds(*con_predicates: ConstructivePredicate) -> ConstructivePredicate elif kw["max_value"] == base["max_value"]: base["exclude_max"] |= kw.get("exclude_max", False) - has_len = {"len" in kw for kw, _ in con_predicates} - assert len(has_len) == 1, "can't mix numeric with length constraints" + has_len = {"len" in kw for kw, _ in con_predicates if kw} + assert len(has_len) <= 1, "can't mix numeric with length constraints" if has_len == {True}: base["len"] = True diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py index e8f8f21ba4..75de4a82ec 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py @@ -23,7 +23,7 @@ from hypothesis.strategies._internal.strategies import ( T4, T5, Ex, - MappedSearchStrategy, + MappedStrategy, SearchStrategy, T, check_strategy, @@ -211,6 +211,9 @@ class ListStrategy(SearchStrategy): new = copy.copy(self) new.min_size = max(self.min_size, kwargs.get("min_value", self.min_size)) new.max_size = min(self.max_size, kwargs.get("max_value", self.max_size)) + # Unsatisfiable filters are easiest to understand without rewriting. + if new.min_size > new.max_size: + return SearchStrategy.filter(self, condition) # Recompute average size; this is cheaper than making it into a property. new.average_size = min( max(new.min_size * 2, new.min_size + 5), @@ -302,7 +305,7 @@ class UniqueSampledListStrategy(UniqueListStrategy): return result -class FixedKeysDictStrategy(MappedSearchStrategy): +class FixedKeysDictStrategy(MappedStrategy): """A strategy which produces dicts with a fixed set of keys, given a strategy for each of their equivalent values. @@ -311,9 +314,12 @@ class FixedKeysDictStrategy(MappedSearchStrategy): """ def __init__(self, strategy_dict): - self.dict_type = type(strategy_dict) + dict_type = type(strategy_dict) self.keys = tuple(strategy_dict.keys()) - super().__init__(strategy=TupleStrategy(strategy_dict[k] for k in self.keys)) + super().__init__( + strategy=TupleStrategy(strategy_dict[k] for k in self.keys), + pack=lambda value: dict_type(zip(self.keys, value)), + ) def calc_is_empty(self, recur): return recur(self.mapped_strategy) @@ -321,9 +327,6 @@ class FixedKeysDictStrategy(MappedSearchStrategy): def __repr__(self): return f"FixedKeysDictStrategy({self.keys!r}, {self.mapped_strategy!r})" - def pack(self, value): - return self.dict_type(zip(self.keys, value)) - class FixedAndOptionalKeysDictStrategy(SearchStrategy): """A strategy which produces dicts with a fixed set of keys, given a diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py index d6bb13c7c1..8f887293e6 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py @@ -61,10 +61,6 @@ def unwrap_strategies(s): assert unwrap_depth >= 0 -def _repr_filter(condition): - return f".filter({get_pretty_function_description(condition)})" - - class LazyStrategy(SearchStrategy): """A strategy which is defined purely by conversion to and from another strategy. @@ -72,14 +68,14 @@ class LazyStrategy(SearchStrategy): Its parameter and distribution come from that other strategy. """ - def __init__(self, function, args, kwargs, filters=(), *, force_repr=None): + def __init__(self, function, args, kwargs, *, transforms=(), force_repr=None): super().__init__() self.__wrapped_strategy = None self.__representation = force_repr self.function = function self.__args = args self.__kwargs = kwargs - self.__filters = filters + self._transformations = transforms @property def supports_find(self): @@ -115,23 +111,28 @@ class LazyStrategy(SearchStrategy): self.__wrapped_strategy = self.function( *unwrapped_args, **unwrapped_kwargs ) - for f in self.__filters: - self.__wrapped_strategy = self.__wrapped_strategy.filter(f) + for method, fn in self._transformations: + self.__wrapped_strategy = getattr(self.__wrapped_strategy, method)(fn) return self.__wrapped_strategy - def filter(self, condition): - try: - repr_ = f"{self!r}{_repr_filter(condition)}" - except Exception: - repr_ = None - return LazyStrategy( + def __with_transform(self, method, fn): + repr_ = self.__representation + if repr_: + repr_ = f"{repr_}.{method}({get_pretty_function_description(fn)})" + return type(self)( self.function, self.__args, self.__kwargs, - (*self.__filters, condition), + transforms=(*self._transformations, (method, fn)), force_repr=repr_, ) + def map(self, pack): + return self.__with_transform("map", pack) + + def filter(self, condition): + return self.__with_transform("filter", condition) + def do_validate(self): w = self.wrapped_strategy assert isinstance(w, SearchStrategy), f"{self!r} returned non-strategy {w!r}" @@ -156,7 +157,10 @@ class LazyStrategy(SearchStrategy): } self.__representation = repr_call( self.function, _args, kwargs_for_repr, reorder=False - ) + "".join(map(_repr_filter, self.__filters)) + ) + "".join( + f".{method}({get_pretty_function_description(fn)})" + for method, fn in self._transformations + ) return self.__representation def do_draw(self, data): diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py index af2fa72937..46d4005cdb 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py @@ -11,6 +11,7 @@ import sys import warnings from collections import abc, defaultdict +from functools import lru_cache from random import shuffle from typing import ( Any, @@ -60,7 +61,7 @@ T5 = TypeVar("T5") calculating = UniqueIdentifier("calculating") MAPPED_SEARCH_STRATEGY_DO_DRAW_LABEL = calc_label_from_name( - "another attempted draw in MappedSearchStrategy" + "another attempted draw in MappedStrategy" ) FILTERED_SEARCH_STRATEGY_DO_DRAW_LABEL = calc_label_from_name( @@ -346,7 +347,7 @@ class SearchStrategy(Generic[Ex]): """ if is_identity_function(pack): return self # type: ignore # Mypy has no way to know that `Ex == T` - return MappedSearchStrategy(pack=pack, strategy=self) + return MappedStrategy(self, pack=pack) def flatmap( self, expand: Callable[[Ex], "SearchStrategy[T]"] @@ -468,9 +469,6 @@ class SampledFromStrategy(SearchStrategy): """A strategy which samples from a set of elements. This is essentially equivalent to using a OneOfStrategy over Just strategies but may be more efficient and convenient. - - The conditional distribution chooses uniformly at random from some - non-empty subset of the elements. """ _MAX_FILTER_CALLS = 10_000 @@ -521,7 +519,10 @@ class SampledFromStrategy(SearchStrategy): # Used in UniqueSampledListStrategy for name, f in self._transformations: if name == "map": - element = f(element) + result = f(element) + if build_context := _current_build_context.value: + build_context.record_call(result, f, [element], {}) + element = result else: assert name == "filter" if not f(element): @@ -794,18 +795,17 @@ def one_of( return OneOfStrategy(args) -class MappedSearchStrategy(SearchStrategy[Ex]): +class MappedStrategy(SearchStrategy[Ex]): """A strategy which is defined purely by conversion to and from another strategy. Its parameter and distribution come from that other strategy. """ - def __init__(self, strategy, pack=None): + def __init__(self, strategy, pack): super().__init__() self.mapped_strategy = strategy - if pack is not None: - self.pack = pack + self.pack = pack def calc_is_empty(self, recur): return recur(self.mapped_strategy) @@ -821,11 +821,6 @@ class MappedSearchStrategy(SearchStrategy[Ex]): def do_validate(self): self.mapped_strategy.validate() - def pack(self, x): - """Take a value produced by the underlying mapped_strategy and turn it - into a value suitable for outputting from this strategy.""" - raise NotImplementedError(f"{self.__class__.__name__}.pack()") - def do_draw(self, data: ConjectureData) -> Any: with warnings.catch_warnings(): if isinstance(self.pack, type) and issubclass( @@ -847,10 +842,67 @@ class MappedSearchStrategy(SearchStrategy[Ex]): @property def branches(self) -> List[SearchStrategy[Ex]]: return [ - MappedSearchStrategy(pack=self.pack, strategy=strategy) + MappedStrategy(strategy, pack=self.pack) for strategy in self.mapped_strategy.branches ] + def filter(self, condition: Callable[[Ex], Any]) -> "SearchStrategy[Ex]": + # Includes a special case so that we can rewrite filters on collection + # lengths, when most collections are `st.lists(...).map(the_type)`. + ListStrategy = _list_strategy_type() + if not isinstance(self.mapped_strategy, ListStrategy) or not ( + (isinstance(self.pack, type) and issubclass(self.pack, abc.Collection)) + or self.pack in _collection_ish_functions() + ): + return super().filter(condition) + + # Check whether our inner list strategy can rewrite this filter condition. + # If not, discard the result and _only_ apply a new outer filter. + new = ListStrategy.filter(self.mapped_strategy, condition) + if getattr(new, "filtered_strategy", None) is self.mapped_strategy: + return super().filter(condition) # didn't rewrite + + # Apply a new outer filter even though we rewrote the inner strategy, + # because some collections can change the list length (dict, set, etc). + return FilteredStrategy(type(self)(new, self.pack), conditions=(condition,)) + + +@lru_cache +def _list_strategy_type(): + from hypothesis.strategies._internal.collections import ListStrategy + + return ListStrategy + + +def _collection_ish_functions(): + funcs = [sorted] + if np := sys.modules.get("numpy"): + # c.f. https://numpy.org/doc/stable/reference/routines.array-creation.html + # Probably only `np.array` and `np.asarray` will be used in practice, + # but why should that stop us when we've already gone this far? + funcs += [ + np.empty_like, + np.eye, + np.identity, + np.ones_like, + np.zeros_like, + np.array, + np.asarray, + np.asanyarray, + np.ascontiguousarray, + np.asmatrix, + np.copy, + np.rec.array, + np.rec.fromarrays, + np.rec.fromrecords, + np.diag, + # bonus undocumented functions from tab-completion: + np.asarray_chkfinite, + np.asfarray, + np.asfortranarray, + ] + return funcs + filter_not_satisfied = UniqueIdentifier("filter not satisfied") diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index da7f74708c..e986241037 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 98, 11) +__version_info__ = (6, 98, 12) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index c71ce1c809..33c8057a99 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.98.11) +VERSION(6.98.12) LICENSE(MPL-2.0) diff --git a/contrib/python/sniffio/.dist-info/METADATA b/contrib/python/sniffio/.dist-info/METADATA index 22520c72af..88968aed16 100644 --- a/contrib/python/sniffio/.dist-info/METADATA +++ b/contrib/python/sniffio/.dist-info/METADATA @@ -1,11 +1,12 @@ Metadata-Version: 2.1 Name: sniffio -Version: 1.3.0 +Version: 1.3.1 Summary: Sniff out which async library your code is running under -Home-page: https://github.com/python-trio/sniffio -Author: Nathaniel J. Smith -Author-email: njs@pobox.com +Author-email: "Nathaniel J. Smith" <njs@pobox.com> License: MIT OR Apache-2.0 +Project-URL: Homepage, https://github.com/python-trio/sniffio +Project-URL: Documentation, https://sniffio.readthedocs.io/ +Project-URL: Changelog, https://sniffio.readthedocs.io/en/latest/history.html Keywords: async,trio,asyncio Classifier: License :: OSI Approved :: MIT License Classifier: License :: OSI Approved :: Apache Software License @@ -20,6 +21,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Intended Audience :: Developers Classifier: Development Status :: 5 - Production/Stable Requires-Python: >=3.7 +Description-Content-Type: text/x-rst License-File: LICENSE License-File: LICENSE.APACHE2 License-File: LICENSE.MIT diff --git a/contrib/python/sniffio/sniffio/__init__.py b/contrib/python/sniffio/sniffio/__init__.py index fb3364d7f1..63f2f19e40 100644 --- a/contrib/python/sniffio/sniffio/__init__.py +++ b/contrib/python/sniffio/sniffio/__init__.py @@ -1,8 +1,10 @@ """Top-level package for sniffio.""" __all__ = [ - "current_async_library", "AsyncLibraryNotFoundError", - "current_async_library_cvar" + "current_async_library", + "AsyncLibraryNotFoundError", + "current_async_library_cvar", + "thread_local", ] from ._version import __version__ diff --git a/contrib/python/sniffio/sniffio/_version.py b/contrib/python/sniffio/sniffio/_version.py index 5a5f906bbf..0495d10545 100644 --- a/contrib/python/sniffio/sniffio/_version.py +++ b/contrib/python/sniffio/sniffio/_version.py @@ -1,3 +1,3 @@ # This file is imported from __init__.py and exec'd from setup.py -__version__ = "1.3.0" +__version__ = "1.3.1" diff --git a/contrib/python/sniffio/ya.make b/contrib/python/sniffio/ya.make index d0e376d4ca..165b99c587 100644 --- a/contrib/python/sniffio/ya.make +++ b/contrib/python/sniffio/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.3.0) +VERSION(1.3.1) LICENSE(Apache-2.0 AND MIT) diff --git a/contrib/python/typing-extensions/py3/.dist-info/METADATA b/contrib/python/typing-extensions/py3/.dist-info/METADATA index 863e977c2f..13d06e24b7 100644 --- a/contrib/python/typing-extensions/py3/.dist-info/METADATA +++ b/contrib/python/typing-extensions/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: typing_extensions -Version: 4.9.0 +Version: 4.10.0 Summary: Backported and Experimental Type Hints for Python 3.8+ Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing Author-email: "Guido van Rossum, Jukka Lehtosalo, Ćukasz Langa, Michael Lee" <levkivskyi@gmail.com> diff --git a/contrib/python/typing-extensions/py3/typing_extensions.py b/contrib/python/typing-extensions/py3/typing_extensions.py index 1666e96b7e..f3132ea4ae 100644 --- a/contrib/python/typing-extensions/py3/typing_extensions.py +++ b/contrib/python/typing-extensions/py3/typing_extensions.py @@ -83,6 +83,7 @@ __all__ = [ 'TypeAlias', 'TypeAliasType', 'TypeGuard', + 'TypeIs', 'TYPE_CHECKING', 'Never', 'NoReturn', @@ -473,7 +474,7 @@ _EXCLUDED_ATTRS = { "_is_runtime_protocol", "__dict__", "__slots__", "__parameters__", "__orig_bases__", "__module__", "_MutableMapping__marker", "__doc__", "__subclasshook__", "__orig_class__", "__init__", "__new__", - "__protocol_attrs__", "__callable_proto_members_only__", + "__protocol_attrs__", "__non_callable_proto_members__", "__match_args__", } @@ -521,6 +522,22 @@ else: if type(self)._is_protocol: raise TypeError('Protocols cannot be instantiated') + def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, <protocol>)`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + # Inheriting from typing._ProtocolMeta isn't actually desirable, # but is necessary to allow typing.Protocol and typing_extensions.Protocol # to mix without getting TypeErrors about "metaclass conflict" @@ -551,11 +568,6 @@ else: abc.ABCMeta.__init__(cls, *args, **kwargs) if getattr(cls, "_is_protocol", False): cls.__protocol_attrs__ = _get_protocol_attrs(cls) - # PEP 544 prohibits using issubclass() - # with protocols that have non-method members. - cls.__callable_proto_members_only__ = all( - callable(getattr(cls, attr, None)) for attr in cls.__protocol_attrs__ - ) def __subclasscheck__(cls, other): if cls is Protocol: @@ -564,26 +576,23 @@ else: getattr(cls, '_is_protocol', False) and not _allow_reckless_class_checks() ): - if not isinstance(other, type): - # Same error message as for issubclass(1, int). - raise TypeError('issubclass() arg 1 must be a class') + if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) + raise TypeError( + "Instance and class checks can only be used with " + "@runtime_checkable protocols" + ) if ( - not cls.__callable_proto_members_only__ + # this attribute is set by @runtime_checkable: + cls.__non_callable_proto_members__ and cls.__dict__.get("__subclasshook__") is _proto_hook ): - non_method_attrs = sorted( - attr for attr in cls.__protocol_attrs__ - if not callable(getattr(cls, attr, None)) - ) + _type_check_issubclass_arg_1(other) + non_method_attrs = sorted(cls.__non_callable_proto_members__) raise TypeError( "Protocols with non-method members don't support issubclass()." f" Non-method members: {str(non_method_attrs)[1:-1]}." ) - if not getattr(cls, '_is_runtime_protocol', False): - raise TypeError( - "Instance and class checks can only be used with " - "@runtime_checkable protocols" - ) return abc.ABCMeta.__subclasscheck__(cls, other) def __instancecheck__(cls, instance): @@ -610,7 +619,8 @@ else: val = inspect.getattr_static(instance, attr) except AttributeError: break - if val is None and callable(getattr(cls, attr, None)): + # this attribute is set by @runtime_checkable: + if val is None and attr not in cls.__non_callable_proto_members__: break else: return True @@ -678,8 +688,58 @@ else: cls.__init__ = _no_init +if sys.version_info >= (3, 13): + runtime_checkable = typing.runtime_checkable +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol. + + Such protocol can be used with isinstance() and issubclass(). + Raise TypeError if applied to a non-protocol class. + This allows a simple-minded structural check very similar to + one trick ponies in collections.abc such as Iterable. + + For example:: + + @runtime_checkable + class Closable(Protocol): + def close(self): ... + + assert isinstance(open('/some/file'), Closable) + + Warning: this will check only the presence of the required methods, + not their type signatures! + """ + if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False): + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + ' got %r' % cls) + cls._is_runtime_protocol = True + + # Only execute the following block if it's a typing_extensions.Protocol class. + # typing.Protocol classes don't need it. + if isinstance(cls, _ProtocolMeta): + # PEP 544 prohibits using issubclass() + # with protocols that have non-method members. + # See gh-113320 for why we compute this attribute here, + # rather than in `_ProtocolMeta.__init__` + cls.__non_callable_proto_members__ = set() + for attr in cls.__protocol_attrs__: + try: + is_callable = callable(getattr(cls, attr, None)) + except Exception as e: + raise TypeError( + f"Failed to determine whether protocol member {attr!r} " + "is a method member" + ) from e + else: + if not is_callable: + cls.__non_callable_proto_members__.add(attr) + + return cls + + # The "runtime" alias exists for backwards compatibility. -runtime = runtime_checkable = typing.runtime_checkable +runtime = runtime_checkable # Our version of runtime-checkable protocols is faster on Python 3.8-3.11 @@ -815,7 +875,7 @@ else: break class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, *, total=True): + def __new__(cls, name, bases, ns, *, total=True, closed=False): """Create new typed dict class object. This method is called when TypedDict is subclassed, @@ -860,6 +920,7 @@ else: optional_keys = set() readonly_keys = set() mutable_keys = set() + extra_items_type = None for base in bases: base_dict = base.__dict__ @@ -869,6 +930,26 @@ else: optional_keys.update(base_dict.get('__optional_keys__', ())) readonly_keys.update(base_dict.get('__readonly_keys__', ())) mutable_keys.update(base_dict.get('__mutable_keys__', ())) + base_extra_items_type = base_dict.get('__extra_items__', None) + if base_extra_items_type is not None: + extra_items_type = base_extra_items_type + + if closed and extra_items_type is None: + extra_items_type = Never + if closed and "__extra_items__" in own_annotations: + annotation_type = own_annotations.pop("__extra_items__") + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + if Required in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "Required" + ) + if NotRequired in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "NotRequired" + ) + extra_items_type = annotation_type annotations.update(own_annotations) for annotation_key, annotation_type in own_annotations.items(): @@ -883,11 +964,7 @@ else: else: optional_keys.add(annotation_key) if ReadOnly in qualifiers: - if annotation_key in mutable_keys: - raise TypeError( - f"Cannot override mutable key {annotation_key!r}" - " with read-only key" - ) + mutable_keys.discard(annotation_key) readonly_keys.add(annotation_key) else: mutable_keys.add(annotation_key) @@ -900,6 +977,8 @@ else: tp_dict.__mutable_keys__ = frozenset(mutable_keys) if not hasattr(tp_dict, '__total__'): tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items_type return tp_dict __call__ = dict # static method @@ -913,7 +992,7 @@ else: _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) @_ensure_subclassable(lambda bases: (_TypedDict,)) - def TypedDict(typename, fields=_marker, /, *, total=True, **kwargs): + def TypedDict(typename, fields=_marker, /, *, total=True, closed=False, **kwargs): """A simple typed namespace. At runtime it is equivalent to a plain dict. TypedDict creates a dictionary type such that a type checker will expect all @@ -973,6 +1052,9 @@ else: "using the functional syntax, pass an empty dictionary, e.g. " ) + example + "." warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) + if closed is not False and closed is not True: + kwargs["closed"] = closed + closed = False fields = kwargs elif kwargs: raise TypeError("TypedDict takes either a dict or keyword arguments," @@ -994,7 +1076,7 @@ else: # Setting correct module is necessary to make typed dict classes pickleable. ns['__module__'] = module - td = _TypedDictMeta(typename, (), ns, total=total) + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed) td.__orig_bases__ = (TypedDict,) return td @@ -1768,6 +1850,98 @@ else: PEP 647 (User-Defined Type Guards). """) +# 3.13+ +if hasattr(typing, 'TypeIs'): + TypeIs = typing.TypeIs +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_ExtensionsSpecialForm + def TypeIs(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.8 +else: + class _TypeIsForm(_ExtensionsSpecialForm, _root=True): + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeIs = _TypeIsForm( + 'TypeIs', + doc="""Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeGuard`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """) + # Vendored from cpython typing._SpecialFrom class _SpecialForm(typing._Final, _root=True): diff --git a/contrib/python/typing-extensions/py3/ya.make b/contrib/python/typing-extensions/py3/ya.make index 1e65722a16..6a099000e4 100644 --- a/contrib/python/typing-extensions/py3/ya.make +++ b/contrib/python/typing-extensions/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.9.0) +VERSION(4.10.0) LICENSE(PSF-2.0) |