Skip to content

Commit

Permalink
Update contrib/python/typing-extensions/py3 to 4.10.0
Browse files Browse the repository at this point in the history
916f253f987c82c38d1bd0f46b8b969b5bcb5c68
  • Loading branch information
robot-piglet committed Mar 11, 2024
1 parent bf64470 commit 038e282
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 31 deletions.
2 changes: 1 addition & 1 deletion contrib/python/typing-extensions/py3/.dist-info/METADATA
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
232 changes: 203 additions & 29 deletions contrib/python/typing-extensions/py3/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
'TypeAlias',
'TypeAliasType',
'TypeGuard',
'TypeIs',
'TYPE_CHECKING',
'Never',
'NoReturn',
Expand Down Expand Up @@ -473,7 +474,7 @@ def clear_overloads():
"_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__",
}

Expand Down Expand Up @@ -521,6 +522,22 @@ def _no_init(self, *args, **kwargs):
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"
Expand Down Expand Up @@ -551,11 +568,6 @@ def __init__(cls, *args, **kwargs):
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:
Expand All @@ -564,26 +576,23 @@ def __subclasscheck__(cls, other):
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):
Expand All @@ -610,7 +619,8 @@ def __instancecheck__(cls, instance):
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
Expand Down Expand Up @@ -678,8 +688,58 @@ def __init_subclass__(cls, *args, **kwargs):
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
Expand Down Expand Up @@ -815,7 +875,7 @@ def _get_typeddict_qualifiers(annotation_type):
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,
Expand Down Expand Up @@ -860,6 +920,7 @@ def __new__(cls, name, bases, ns, *, total=True):
optional_keys = set()
readonly_keys = set()
mutable_keys = set()
extra_items_type = None

for base in bases:
base_dict = base.__dict__
Expand All @@ -869,6 +930,26 @@ def __new__(cls, name, bases, ns, *, total=True):
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():
Expand All @@ -883,11 +964,7 @@ def __new__(cls, name, bases, ns, *, total=True):
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)
Expand All @@ -900,6 +977,8 @@ def __new__(cls, name, bases, ns, *, total=True):
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
Expand All @@ -913,7 +992,7 @@ def __subclasscheck__(cls, other):
_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
Expand Down Expand Up @@ -973,6 +1052,9 @@ class Point2D(TypedDict):
"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,"
Expand All @@ -994,7 +1076,7 @@ class Point2D(TypedDict):
# 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

Expand Down Expand Up @@ -1768,6 +1850,98 @@ def is_str(val: Union[str, float]):
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):
Expand Down
2 changes: 1 addition & 1 deletion contrib/python/typing-extensions/py3/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PY3_LIBRARY()

VERSION(4.9.0)
VERSION(4.10.0)

LICENSE(PSF-2.0)

Expand Down

0 comments on commit 038e282

Please sign in to comment.