c10-beta
imports/c10-beta/python-typing-extensions-4.9.0-5.el10
commit
05d43d6529
@ -0,0 +1 @@
|
||||
SOURCES/typing_extensions-4.9.0.tar.gz
|
@ -0,0 +1 @@
|
||||
c43802607d8b5c1d102401e654fcab70609fb7bc SOURCES/typing_extensions-4.9.0.tar.gz
|
@ -0,0 +1,701 @@
|
||||
From 07a30791562f626c63d3f85a5c87dd411e3108cc Mon Sep 17 00:00:00 2001
|
||||
From: AlexWaygood <alex.waygood@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 13:19:37 +0000
|
||||
Subject: [PATCH 1/6] Backport recent improvements to the implementation of
|
||||
`Protocol`
|
||||
|
||||
---
|
||||
CHANGELOG.md | 11 +++++
|
||||
src/test_typing_extensions.py | 73 +++++++++++++++++++++-------
|
||||
src/typing_extensions.py | 91 +++++++++++++++++++++++++++--------
|
||||
3 files changed, 136 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index fedc2a3f..c3957a29 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -1,3 +1,14 @@
|
||||
+# Unreleased
|
||||
+
|
||||
+- Speedup `issubclass()` checks against simple runtime-checkable protocols by
|
||||
+ around 6% (backporting https://github.com/python/cpython/pull/112717, by Alex
|
||||
+ Waygood).
|
||||
+- Fix a regression in the implementation of protocols where `typing.Protocol`
|
||||
+ classes that were not marked as `@runtime-checkable` would be unnecessarily
|
||||
+ introspected, potentially causing exceptions to be raised if the protocol had
|
||||
+ problematic members. Patch by Alex Waygood, backporting
|
||||
+ https://github.com/python/cpython/pull/113401.
|
||||
+
|
||||
# Release 4.9.0 (December 9, 2023)
|
||||
|
||||
This feature release adds `typing_extensions.ReadOnly`, as specified
|
||||
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
|
||||
index 77876b7f..11208d95 100644
|
||||
--- a/src/test_typing_extensions.py
|
||||
+++ b/src/test_typing_extensions.py
|
||||
@@ -1872,18 +1872,6 @@ class E(C, BP): pass
|
||||
self.assertNotIsInstance(D(), E)
|
||||
self.assertNotIsInstance(E(), D)
|
||||
|
||||
- def test_runtimecheckable_on_typing_dot_Protocol(self):
|
||||
- @runtime_checkable
|
||||
- class Foo(typing.Protocol):
|
||||
- x: int
|
||||
-
|
||||
- class Bar:
|
||||
- def __init__(self):
|
||||
- self.x = 42
|
||||
-
|
||||
- self.assertIsInstance(Bar(), Foo)
|
||||
- self.assertNotIsInstance(object(), Foo)
|
||||
-
|
||||
def test_typing_dot_runtimecheckable_on_Protocol(self):
|
||||
@typing.runtime_checkable
|
||||
class Foo(Protocol):
|
||||
@@ -2817,8 +2805,8 @@ def meth(self): pass # noqa: B027
|
||||
|
||||
self.assertNotIn("__protocol_attrs__", vars(NonP))
|
||||
self.assertNotIn("__protocol_attrs__", vars(NonPR))
|
||||
- self.assertNotIn("__callable_proto_members_only__", vars(NonP))
|
||||
- self.assertNotIn("__callable_proto_members_only__", vars(NonPR))
|
||||
+ self.assertNotIn("__non_callable_proto_members__", vars(NonP))
|
||||
+ self.assertNotIn("__non_callable_proto_members__", vars(NonPR))
|
||||
|
||||
acceptable_extra_attrs = {
|
||||
'_is_protocol', '_is_runtime_protocol', '__parameters__',
|
||||
@@ -2891,11 +2879,26 @@ def __subclasshook__(cls, other):
|
||||
@skip_if_py312b1
|
||||
def test_issubclass_fails_correctly(self):
|
||||
@runtime_checkable
|
||||
- class P(Protocol):
|
||||
+ class NonCallableMembers(Protocol):
|
||||
x = 1
|
||||
+
|
||||
+ class NotRuntimeCheckable(Protocol):
|
||||
+ def callable_member(self) -> int: ...
|
||||
+
|
||||
+ @runtime_checkable
|
||||
+ class RuntimeCheckable(Protocol):
|
||||
+ def callable_member(self) -> int: ...
|
||||
+
|
||||
class C: pass
|
||||
- with self.assertRaisesRegex(TypeError, r"issubclass\(\) arg 1 must be a class"):
|
||||
- issubclass(C(), P)
|
||||
+
|
||||
+ # These three all exercise different code paths,
|
||||
+ # but should result in the same error message:
|
||||
+ for protocol in NonCallableMembers, NotRuntimeCheckable, RuntimeCheckable:
|
||||
+ with self.subTest(proto_name=protocol.__name__):
|
||||
+ with self.assertRaisesRegex(
|
||||
+ TypeError, r"issubclass\(\) arg 1 must be a class"
|
||||
+ ):
|
||||
+ issubclass(C(), protocol)
|
||||
|
||||
def test_defining_generic_protocols(self):
|
||||
T = TypeVar('T')
|
||||
@@ -3456,6 +3459,7 @@ def method(self) -> None: ...
|
||||
|
||||
@skip_if_early_py313_alpha
|
||||
def test_protocol_issubclass_error_message(self):
|
||||
+ @runtime_checkable
|
||||
class Vec2D(Protocol):
|
||||
x: float
|
||||
y: float
|
||||
@@ -3471,6 +3475,39 @@ def square_norm(self) -> float:
|
||||
with self.assertRaisesRegex(TypeError, re.escape(expected_error_message)):
|
||||
issubclass(int, Vec2D)
|
||||
|
||||
+ def test_nonruntime_protocol_interaction_with_evil_classproperty(self):
|
||||
+ class classproperty:
|
||||
+ def __get__(self, instance, type):
|
||||
+ raise RuntimeError("NO")
|
||||
+
|
||||
+ class Commentable(Protocol):
|
||||
+ evil = classproperty()
|
||||
+
|
||||
+ # recognised as a protocol attr,
|
||||
+ # but not actually accessed by the protocol metaclass
|
||||
+ # (which would raise RuntimeError) for non-runtime protocols.
|
||||
+ # See gh-113320
|
||||
+ self.assertEqual(get_protocol_members(Commentable), {"evil"})
|
||||
+
|
||||
+ def test_runtime_protocol_interaction_with_evil_classproperty(self):
|
||||
+ class CustomError(Exception): pass
|
||||
+
|
||||
+ class classproperty:
|
||||
+ def __get__(self, instance, type):
|
||||
+ raise CustomError
|
||||
+
|
||||
+ with self.assertRaises(TypeError) as cm:
|
||||
+ @runtime_checkable
|
||||
+ class Commentable(Protocol):
|
||||
+ evil = classproperty()
|
||||
+
|
||||
+ exc = cm.exception
|
||||
+ self.assertEqual(
|
||||
+ exc.args[0],
|
||||
+ "Failed to determine whether protocol member 'evil' is a method member"
|
||||
+ )
|
||||
+ self.assertIs(type(exc.__cause__), CustomError)
|
||||
+
|
||||
|
||||
class Point2DGeneric(Generic[T], TypedDict):
|
||||
a: T
|
||||
@@ -5263,7 +5300,7 @@ def test_typing_extensions_defers_when_possible(self):
|
||||
'SupportsRound', 'Unpack',
|
||||
}
|
||||
if sys.version_info < (3, 13):
|
||||
- exclude |= {'NamedTuple', 'Protocol'}
|
||||
+ exclude |= {'NamedTuple', 'Protocol', 'runtime_checkable'}
|
||||
if not hasattr(typing, 'ReadOnly'):
|
||||
exclude |= {'TypedDict', 'is_typeddict'}
|
||||
for item in typing_extensions.__all__:
|
||||
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
|
||||
index 1666e96b..7de29cd5 100644
|
||||
--- a/src/typing_extensions.py
|
||||
+++ b/src/typing_extensions.py
|
||||
@@ -473,7 +473,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__",
|
||||
}
|
||||
|
||||
@@ -521,6 +521,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"
|
||||
@@ -551,11 +567,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:
|
||||
@@ -564,26 +575,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):
|
||||
@@ -610,7 +618,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
|
||||
@@ -678,8 +687,48 @@ 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
|
||||
+ # 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
|
||||
|
||||
From c9fdbc103c1a707669932ee42ce52e375582c6db Mon Sep 17 00:00:00 2001
|
||||
From: AlexWaygood <alex.waygood@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 15:06:34 +0000
|
||||
Subject: [PATCH 2/6] Restore being able to use
|
||||
`typing_extensions.runtime_checkable` on `typing.Protocol`
|
||||
|
||||
---
|
||||
src/test_typing_extensions.py | 12 ++++
|
||||
src/typing_extensions.py | 125 +++++++++++++++++-----------------
|
||||
2 files changed, 75 insertions(+), 62 deletions(-)
|
||||
|
||||
diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py
|
||||
index 11208d95..58dc1851 100644
|
||||
--- a/src/test_typing_extensions.py
|
||||
+++ b/src/test_typing_extensions.py
|
||||
@@ -1872,6 +1872,18 @@ class E(C, BP): pass
|
||||
self.assertNotIsInstance(D(), E)
|
||||
self.assertNotIsInstance(E(), D)
|
||||
|
||||
+ def test_runtimecheckable_on_typing_dot_Protocol(self):
|
||||
+ @runtime_checkable
|
||||
+ class Foo(typing.Protocol):
|
||||
+ x: int
|
||||
+
|
||||
+ class Bar:
|
||||
+ def __init__(self):
|
||||
+ self.x = 42
|
||||
+
|
||||
+ self.assertIsInstance(Bar(), Foo)
|
||||
+ self.assertNotIsInstance(object(), Foo)
|
||||
+
|
||||
def test_typing_dot_runtimecheckable_on_Protocol(self):
|
||||
@typing.runtime_checkable
|
||||
class Foo(Protocol):
|
||||
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
|
||||
index 7de29cd5..10c5795d 100644
|
||||
--- a/src/typing_extensions.py
|
||||
+++ b/src/typing_extensions.py
|
||||
@@ -687,6 +687,52 @@ def __init_subclass__(cls, *args, **kwargs):
|
||||
cls.__init__ = _no_init
|
||||
|
||||
|
||||
+if hasattr(typing, "is_protocol"):
|
||||
+ is_protocol = typing.is_protocol
|
||||
+ get_protocol_members = typing.get_protocol_members
|
||||
+else:
|
||||
+ def is_protocol(tp: type, /) -> bool:
|
||||
+ """Return True if the given type is a Protocol.
|
||||
+
|
||||
+ Example::
|
||||
+
|
||||
+ >>> from typing_extensions import Protocol, is_protocol
|
||||
+ >>> class P(Protocol):
|
||||
+ ... def a(self) -> str: ...
|
||||
+ ... b: int
|
||||
+ >>> is_protocol(P)
|
||||
+ True
|
||||
+ >>> is_protocol(int)
|
||||
+ False
|
||||
+ """
|
||||
+ return (
|
||||
+ isinstance(tp, type)
|
||||
+ and getattr(tp, '_is_protocol', False)
|
||||
+ and tp is not Protocol
|
||||
+ and tp is not typing.Protocol
|
||||
+ )
|
||||
+
|
||||
+ def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
|
||||
+ """Return the set of members defined in a Protocol.
|
||||
+
|
||||
+ Example::
|
||||
+
|
||||
+ >>> from typing_extensions import Protocol, get_protocol_members
|
||||
+ >>> class P(Protocol):
|
||||
+ ... def a(self) -> str: ...
|
||||
+ ... b: int
|
||||
+ >>> get_protocol_members(P)
|
||||
+ frozenset({'a', 'b'})
|
||||
+
|
||||
+ Raise a TypeError for arguments that are not Protocols.
|
||||
+ """
|
||||
+ if not is_protocol(tp):
|
||||
+ raise TypeError(f'{tp!r} is not a Protocol')
|
||||
+ if hasattr(tp, '__protocol_attrs__'):
|
||||
+ return frozenset(tp.__protocol_attrs__)
|
||||
+ return frozenset(_get_protocol_attrs(tp))
|
||||
+
|
||||
+
|
||||
if sys.version_info >= (3, 13):
|
||||
runtime_checkable = typing.runtime_checkable
|
||||
else:
|
||||
@@ -708,22 +754,23 @@ def close(self): ...
|
||||
raise TypeError('@runtime_checkable can be only applied to protocol classes,'
|
||||
' got %r' % cls)
|
||||
cls._is_runtime_protocol = True
|
||||
- # 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)
|
||||
+ 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 get_protocol_members(cls):
|
||||
+ 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
|
||||
|
||||
|
||||
@@ -2978,52 +3025,6 @@ def __ror__(self, left):
|
||||
return typing.Union[left, self]
|
||||
|
||||
|
||||
-if hasattr(typing, "is_protocol"):
|
||||
- is_protocol = typing.is_protocol
|
||||
- get_protocol_members = typing.get_protocol_members
|
||||
-else:
|
||||
- def is_protocol(tp: type, /) -> bool:
|
||||
- """Return True if the given type is a Protocol.
|
||||
-
|
||||
- Example::
|
||||
-
|
||||
- >>> from typing_extensions import Protocol, is_protocol
|
||||
- >>> class P(Protocol):
|
||||
- ... def a(self) -> str: ...
|
||||
- ... b: int
|
||||
- >>> is_protocol(P)
|
||||
- True
|
||||
- >>> is_protocol(int)
|
||||
- False
|
||||
- """
|
||||
- return (
|
||||
- isinstance(tp, type)
|
||||
- and getattr(tp, '_is_protocol', False)
|
||||
- and tp is not Protocol
|
||||
- and tp is not typing.Protocol
|
||||
- )
|
||||
-
|
||||
- def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
|
||||
- """Return the set of members defined in a Protocol.
|
||||
-
|
||||
- Example::
|
||||
-
|
||||
- >>> from typing_extensions import Protocol, get_protocol_members
|
||||
- >>> class P(Protocol):
|
||||
- ... def a(self) -> str: ...
|
||||
- ... b: int
|
||||
- >>> get_protocol_members(P)
|
||||
- frozenset({'a', 'b'})
|
||||
-
|
||||
- Raise a TypeError for arguments that are not Protocols.
|
||||
- """
|
||||
- if not is_protocol(tp):
|
||||
- raise TypeError(f'{tp!r} is not a Protocol')
|
||||
- if hasattr(tp, '__protocol_attrs__'):
|
||||
- return frozenset(tp.__protocol_attrs__)
|
||||
- return frozenset(_get_protocol_attrs(tp))
|
||||
-
|
||||
-
|
||||
if hasattr(typing, "Doc"):
|
||||
Doc = typing.Doc
|
||||
else:
|
||||
|
||||
From 8dd0b5b51ebfa428d21f2a1d4639ecec9b3a15e0 Mon Sep 17 00:00:00 2001
|
||||
From: AlexWaygood <alex.waygood@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 15:08:31 +0000
|
||||
Subject: [PATCH 3/6] reduce diff
|
||||
|
||||
---
|
||||
src/typing_extensions.py | 94 ++++++++++++++++++++--------------------
|
||||
1 file changed, 47 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
|
||||
index 10c5795d..1f03f15e 100644
|
||||
--- a/src/typing_extensions.py
|
||||
+++ b/src/typing_extensions.py
|
||||
@@ -687,52 +687,6 @@ def __init_subclass__(cls, *args, **kwargs):
|
||||
cls.__init__ = _no_init
|
||||
|
||||
|
||||
-if hasattr(typing, "is_protocol"):
|
||||
- is_protocol = typing.is_protocol
|
||||
- get_protocol_members = typing.get_protocol_members
|
||||
-else:
|
||||
- def is_protocol(tp: type, /) -> bool:
|
||||
- """Return True if the given type is a Protocol.
|
||||
-
|
||||
- Example::
|
||||
-
|
||||
- >>> from typing_extensions import Protocol, is_protocol
|
||||
- >>> class P(Protocol):
|
||||
- ... def a(self) -> str: ...
|
||||
- ... b: int
|
||||
- >>> is_protocol(P)
|
||||
- True
|
||||
- >>> is_protocol(int)
|
||||
- False
|
||||
- """
|
||||
- return (
|
||||
- isinstance(tp, type)
|
||||
- and getattr(tp, '_is_protocol', False)
|
||||
- and tp is not Protocol
|
||||
- and tp is not typing.Protocol
|
||||
- )
|
||||
-
|
||||
- def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
|
||||
- """Return the set of members defined in a Protocol.
|
||||
-
|
||||
- Example::
|
||||
-
|
||||
- >>> from typing_extensions import Protocol, get_protocol_members
|
||||
- >>> class P(Protocol):
|
||||
- ... def a(self) -> str: ...
|
||||
- ... b: int
|
||||
- >>> get_protocol_members(P)
|
||||
- frozenset({'a', 'b'})
|
||||
-
|
||||
- Raise a TypeError for arguments that are not Protocols.
|
||||
- """
|
||||
- if not is_protocol(tp):
|
||||
- raise TypeError(f'{tp!r} is not a Protocol')
|
||||
- if hasattr(tp, '__protocol_attrs__'):
|
||||
- return frozenset(tp.__protocol_attrs__)
|
||||
- return frozenset(_get_protocol_attrs(tp))
|
||||
-
|
||||
-
|
||||
if sys.version_info >= (3, 13):
|
||||
runtime_checkable = typing.runtime_checkable
|
||||
else:
|
||||
@@ -760,7 +714,7 @@ def close(self): ...
|
||||
# See gh-113320 for why we compute this attribute here,
|
||||
# rather than in `_ProtocolMeta.__init__`
|
||||
cls.__non_callable_proto_members__ = set()
|
||||
- for attr in get_protocol_members(cls):
|
||||
+ for attr in cls.__protocol_attrs__:
|
||||
try:
|
||||
is_callable = callable(getattr(cls, attr, None))
|
||||
except Exception as e:
|
||||
@@ -3025,6 +2979,52 @@ def __ror__(self, left):
|
||||
return typing.Union[left, self]
|
||||
|
||||
|
||||
+if hasattr(typing, "is_protocol"):
|
||||
+ is_protocol = typing.is_protocol
|
||||
+ get_protocol_members = typing.get_protocol_members
|
||||
+else:
|
||||
+ def is_protocol(tp: type, /) -> bool:
|
||||
+ """Return True if the given type is a Protocol.
|
||||
+
|
||||
+ Example::
|
||||
+
|
||||
+ >>> from typing_extensions import Protocol, is_protocol
|
||||
+ >>> class P(Protocol):
|
||||
+ ... def a(self) -> str: ...
|
||||
+ ... b: int
|
||||
+ >>> is_protocol(P)
|
||||
+ True
|
||||
+ >>> is_protocol(int)
|
||||
+ False
|
||||
+ """
|
||||
+ return (
|
||||
+ isinstance(tp, type)
|
||||
+ and getattr(tp, '_is_protocol', False)
|
||||
+ and tp is not Protocol
|
||||
+ and tp is not typing.Protocol
|
||||
+ )
|
||||
+
|
||||
+ def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]:
|
||||
+ """Return the set of members defined in a Protocol.
|
||||
+
|
||||
+ Example::
|
||||
+
|
||||
+ >>> from typing_extensions import Protocol, get_protocol_members
|
||||
+ >>> class P(Protocol):
|
||||
+ ... def a(self) -> str: ...
|
||||
+ ... b: int
|
||||
+ >>> get_protocol_members(P)
|
||||
+ frozenset({'a', 'b'})
|
||||
+
|
||||
+ Raise a TypeError for arguments that are not Protocols.
|
||||
+ """
|
||||
+ if not is_protocol(tp):
|
||||
+ raise TypeError(f'{tp!r} is not a Protocol')
|
||||
+ if hasattr(tp, '__protocol_attrs__'):
|
||||
+ return frozenset(tp.__protocol_attrs__)
|
||||
+ return frozenset(_get_protocol_attrs(tp))
|
||||
+
|
||||
+
|
||||
if hasattr(typing, "Doc"):
|
||||
Doc = typing.Doc
|
||||
else:
|
||||
|
||||
From 0d92d3bda68a1e016b1f30641e6250260d253138 Mon Sep 17 00:00:00 2001
|
||||
From: AlexWaygood <alex.waygood@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 15:10:01 +0000
|
||||
Subject: [PATCH 4/6] add comment
|
||||
|
||||
---
|
||||
src/typing_extensions.py | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
|
||||
index 1f03f15e..1e7e9e33 100644
|
||||
--- a/src/typing_extensions.py
|
||||
+++ b/src/typing_extensions.py
|
||||
@@ -708,6 +708,9 @@ def close(self): ...
|
||||
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.
|
||||
@@ -725,6 +728,7 @@ def close(self): ...
|
||||
else:
|
||||
if not is_callable:
|
||||
cls.__non_callable_proto_members__.add(attr)
|
||||
+
|
||||
return cls
|
||||
|
||||
|
||||
|
||||
From ecd711bfe88f4839797311cdee1956daaa58d6c1 Mon Sep 17 00:00:00 2001
|
||||
From: AlexWaygood <alex.waygood@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 15:11:57 +0000
|
||||
Subject: [PATCH 5/6] fix docstring
|
||||
|
||||
---
|
||||
src/typing_extensions.py | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/src/typing_extensions.py b/src/typing_extensions.py
|
||||
index 1e7e9e33..4007594c 100644
|
||||
--- a/src/typing_extensions.py
|
||||
+++ b/src/typing_extensions.py
|
||||
@@ -692,15 +692,20 @@ def __init_subclass__(cls, *args, **kwargs):
|
||||
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!
|
||||
"""
|
||||
|
||||
From c858502ddf1bf3474511c29b871400539f1c0acc Mon Sep 17 00:00:00 2001
|
||||
From: Jelle Zijlstra <jelle.zijlstra@gmail.com>
|
||||
Date: Sat, 20 Jan 2024 09:55:03 -0800
|
||||
Subject: [PATCH 6/6] Update CHANGELOG.md
|
||||
|
||||
---
|
||||
CHANGELOG.md | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/CHANGELOG.md b/CHANGELOG.md
|
||||
index c3957a29..be1c16d6 100644
|
||||
--- a/CHANGELOG.md
|
||||
+++ b/CHANGELOG.md
|
||||
@@ -4,7 +4,7 @@
|
||||
around 6% (backporting https://github.com/python/cpython/pull/112717, by Alex
|
||||
Waygood).
|
||||
- Fix a regression in the implementation of protocols where `typing.Protocol`
|
||||
- classes that were not marked as `@runtime-checkable` would be unnecessarily
|
||||
+ classes that were not marked as `@runtime_checkable` would be unnecessarily
|
||||
introspected, potentially causing exceptions to be raised if the protocol had
|
||||
problematic members. Patch by Alex Waygood, backporting
|
||||
https://github.com/python/cpython/pull/113401.
|
@ -0,0 +1,194 @@
|
||||
Name: python-typing-extensions
|
||||
Version: 4.9.0
|
||||
Release: 5%{?dist}
|
||||
Summary: Python Typing Extensions
|
||||
|
||||
License: PSF-2.0
|
||||
URL: https://pypi.org/project/typing-extensions/
|
||||
Source0: %{pypi_source typing_extensions}
|
||||
# fix test_generic_protocols_special_from_protocol with latest Python
|
||||
Patch0: https://github.com/python/typing_extensions/pull/324.patch
|
||||
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: python3-devel
|
||||
BuildRequires: python3-test
|
||||
|
||||
|
||||
%global _description %{expand:
|
||||
The typing_extensions module serves two related purposes:
|
||||
|
||||
Enable use of new type system features on older Python versions.
|
||||
For example, typing.TypeGuard is new in Python 3.10, but typing_extensions
|
||||
allows users on previous Python versions to use it too.
|
||||
|
||||
Enable experimentation with new type system PEPs before they are accepted and
|
||||
added to the typing module.
|
||||
|
||||
typing_extensions is treated specially by static type checkers such as mypy and
|
||||
pyright. Objects defined in typing_extensions are treated the same way as
|
||||
equivalent forms in typing.
|
||||
|
||||
typing_extensions uses Semantic Versioning. The major version will be
|
||||
incremented only for backwards-incompatible changes. Therefore, it's safe to
|
||||
depend on typing_extensions like this: typing_extensions >=x.y, <(x+1),
|
||||
where x.y is the first version that includes all features you need.}
|
||||
|
||||
%description %_description
|
||||
|
||||
%package -n python3-typing-extensions
|
||||
Summary: %{summary}
|
||||
|
||||
%description -n python3-typing-extensions %_description
|
||||
|
||||
|
||||
%prep
|
||||
%autosetup -n typing_extensions-%{version} -p1
|
||||
|
||||
|
||||
%generate_buildrequires
|
||||
%pyproject_buildrequires
|
||||
|
||||
|
||||
%build
|
||||
%pyproject_wheel
|
||||
|
||||
|
||||
%install
|
||||
%pyproject_install
|
||||
|
||||
%pyproject_save_files typing_extensions
|
||||
|
||||
|
||||
%check
|
||||
cd src
|
||||
%{python3} -m unittest discover
|
||||
|
||||
|
||||
%files -n python3-typing-extensions -f %{pyproject_files}
|
||||
%license LICENSE
|
||||
%doc CHANGELOG.md
|
||||
%doc README.md
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 4.9.0-5
|
||||
- Bump release for June 2024 mass rebuild
|
||||
|
||||
* Fri Feb 16 2024 Yaakov Selkowitz <yselkowi@redhat.com> - 4.9.0-4
|
||||
- Fix test_generic_protocols_special_from_protocol with latest Python
|
||||
|
||||
* Fri Jan 26 2024 Fedora Release Engineering <releng@fedoraproject.org> - 4.9.0-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Mon Jan 22 2024 Fedora Release Engineering <releng@fedoraproject.org> - 4.9.0-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
|
||||
|
||||
* Sun Dec 10 2023 Jonny Heggheim <hegjon@gmail.com> - 4.9.0-1
|
||||
- Updated to version 4.9.0
|
||||
|
||||
* Mon Sep 18 2023 Jonny Heggheim <hegjon@gmail.com> - 4.8.0-1
|
||||
- Updated to version 4.8.0
|
||||
|
||||
* Fri Jul 21 2023 Fedora Release Engineering <releng@fedoraproject.org> - 4.7.1-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
|
||||
|
||||
* Sun Jul 02 2023 Jonny Heggheim <hegjon@gmail.com> - 4.7.1-1
|
||||
- Updated to version 4.7.1
|
||||
|
||||
* Thu Jun 29 2023 Python Maint <python-maint@redhat.com> - 4.7.0-2
|
||||
- Rebuilt for Python 3.12
|
||||
|
||||
* Wed Jun 28 2023 Jonny Heggheim <hegjon@gmail.com> - 4.7.0-1
|
||||
- Updated to version 4.7.0
|
||||
|
||||
* Tue Jun 13 2023 Python Maint <python-maint@redhat.com> - 4.6.3-2
|
||||
- Rebuilt for Python 3.12
|
||||
|
||||
* Sat Jun 03 2023 Jonny Heggheim <hegjon@gmail.com> - 4.6.3-1
|
||||
- Updated to version 4.6.3
|
||||
|
||||
* Thu May 25 2023 Jonny Heggheim <hegjon@gmail.com> - 4.6.2-1
|
||||
- Updated to version 4.6.2
|
||||
|
||||
* Wed May 24 2023 Jonny Heggheim <hegjon@gmail.com> - 4.6.1-1
|
||||
- Updated to version 4.6.1
|
||||
|
||||
* Tue May 23 2023 Jonny Heggheim <hegjon@gmail.com> - 4.6.0-1
|
||||
- Updated to version 4.6.0
|
||||
|
||||
* Wed Feb 15 2023 Jonny Heggheim <hegjon@gmail.com> - 4.5.0-1
|
||||
- Updated to version 4.5.0
|
||||
|
||||
* Fri Jan 20 2023 Fedora Release Engineering <releng@fedoraproject.org> - 4.4.0-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
|
||||
|
||||
* Tue Nov 29 2022 Benjamin A. Beasley <code@musicinmybrain.net> - 4.4.0-2
|
||||
- Update License to SPDX
|
||||
|
||||
* Thu Nov 24 2022 Jonny Heggheim <hegjon@gmail.com> - 4.4.0-1
|
||||
- Updated to version 4.4.0
|
||||
|
||||
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 4.2.0-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
|
||||
|
||||
* Mon Jun 13 2022 Python Maint <python-maint@redhat.com> - 4.2.0-4
|
||||
- Rebuilt for Python 3.11
|
||||
|
||||
* Mon May 23 2022 Benjamin A. Beasley <code@musicinmybrain.net> - 4.2.0-3
|
||||
- Stop using deprecated zero-argument pypi_source macro
|
||||
|
||||
* Sun May 22 2022 Jonny Heggheim <hegjon@gmail.com> - 4.2.0-2
|
||||
- Removed unused build depenencies
|
||||
|
||||
* Sat Apr 30 2022 Jonny Heggheim <hegjon@gmail.com> - 4.2.0-1
|
||||
- Updated to version 4.2.0
|
||||
|
||||
* Fri Jan 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.0.2-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
|
||||
|
||||
* Tue Aug 31 2021 Fabian Affolter <mail@fabian-affolter.ch> - 3.10.0.2-1
|
||||
- Update to latest upstream release 3.10.0.2 (closes rhbz#1955959)
|
||||
|
||||
* Thu Aug 26 2021 Fabian Affolter <mail@fabian-affolter.ch> - 3.10.0.0-1
|
||||
- Update to latest upstream release 3.10.0.0 (closes rhbz#1955959)
|
||||
|
||||
* Fri Jul 23 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.4.3-4
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
|
||||
|
||||
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 3.7.4.3-3
|
||||
- Rebuilt for Python 3.10
|
||||
|
||||
* Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.4.3-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
|
||||
|
||||
* Sun Aug 23 2020 Fabian Affolter <mail@fabian-affolter.ch> - 3.7.4.3-1
|
||||
- Update to latest upstream release 3.7.4.3 (rhbz#1871451)
|
||||
|
||||
* Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.4.2-3
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
|
||||
|
||||
* Tue May 26 2020 Miro Hrončok <mhroncok@redhat.com> - 3.7.4.2-2
|
||||
- Rebuilt for Python 3.9
|
||||
|
||||
* Sat Apr 11 2020 Fabian Affolter <mail@fabian-affolter.ch> - 3.7.4.2-1
|
||||
- Support for Python 3.9 (rhbz#1808663)
|
||||
- Update to latest upstream release 3.7.4.2 (rhbz#1766182)
|
||||
|
||||
* Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.4-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
|
||||
|
||||
* Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 3.7.4-4
|
||||
- Rebuilt for Python 3.8.0rc1 (#1748018)
|
||||
|
||||
* Mon Aug 19 2019 Miro Hrončok <mhroncok@redhat.com> - 3.7.4-3
|
||||
- Rebuilt for Python 3.8
|
||||
|
||||
* Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.4-2
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
|
||||
|
||||
* Fri Jul 12 2019 Jonny Heggheim <hegjon@gmail.com> - 3.7.4-1
|
||||
- Updated to 3.7.4
|
||||
|
||||
* Sun Mar 31 2019 Jonny Heggheim <hegjon@gmail.com> - 3.7.2-1
|
||||
- Inital packaging
|
Loading…
Reference in new issue