aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-03-11 17:59:28 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-03-11 18:10:18 +0300
commita7a431a1c2a6704ba0cf2dbfe9e1c198945e8a9e (patch)
tree04c270807febe5f7da19a2510b450980f671f365 /contrib
parent7fa2009de5a7f9f102480fab66bdd624aa541755 (diff)
downloadydb-a7a431a1c2a6704ba0cf2dbfe9e1c198945e8a9e.tar.gz
Intermediate changes
Diffstat (limited to 'contrib')
-rw-r--r--contrib/python/hypothesis/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/numpy.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/filtering.py13
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py17
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py36
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py84
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/version.py2
-rw-r--r--contrib/python/hypothesis/py3/ya.make2
-rw-r--r--contrib/python/sniffio/.dist-info/METADATA10
-rw-r--r--contrib/python/sniffio/sniffio/__init__.py6
-rw-r--r--contrib/python/sniffio/sniffio/_version.py2
-rw-r--r--contrib/python/sniffio/ya.make2
-rw-r--r--contrib/python/typing-extensions/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/typing-extensions/py3/typing_extensions.py232
-rw-r--r--contrib/python/typing-extensions/py3/ya.make2
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)