mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-132493: lazy evaluation of annotations in typing._proto_hook
(#132534)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: sobolevn <mail@sobolevn.me> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
014c7f9047
commit
71af090e24
3 changed files with 38 additions and 4 deletions
|
@ -4554,6 +4554,33 @@ class ProtocolTests(BaseTestCase):
|
||||||
)
|
)
|
||||||
self.assertIs(type(exc.__cause__), CustomError)
|
self.assertIs(type(exc.__cause__), CustomError)
|
||||||
|
|
||||||
|
def test_isinstance_with_deferred_evaluation_of_annotations(self):
|
||||||
|
@runtime_checkable
|
||||||
|
class P(Protocol):
|
||||||
|
def meth(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class DeferredClass:
|
||||||
|
x: undefined
|
||||||
|
|
||||||
|
class DeferredClassImplementingP:
|
||||||
|
x: undefined | int
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.x = 0
|
||||||
|
|
||||||
|
def meth(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
# override meth with a non-method attribute to make it part of __annotations__ instead of __dict__
|
||||||
|
class SubProtocol(P, Protocol):
|
||||||
|
meth: undefined
|
||||||
|
|
||||||
|
|
||||||
|
self.assertIsSubclass(SubProtocol, P)
|
||||||
|
self.assertNotIsInstance(DeferredClass(), P)
|
||||||
|
self.assertIsInstance(DeferredClassImplementingP(), P)
|
||||||
|
|
||||||
def test_deferred_evaluation_of_annotations(self):
|
def test_deferred_evaluation_of_annotations(self):
|
||||||
class DeferredProto(Protocol):
|
class DeferredProto(Protocol):
|
||||||
x: DoesNotExist
|
x: DoesNotExist
|
||||||
|
|
|
@ -2020,10 +2020,13 @@ def _proto_hook(cls, other):
|
||||||
break
|
break
|
||||||
|
|
||||||
# ...or in annotations, if it is a sub-protocol.
|
# ...or in annotations, if it is a sub-protocol.
|
||||||
annotations = getattr(base, '__annotations__', {})
|
if (
|
||||||
if (isinstance(annotations, collections.abc.Mapping) and
|
issubclass(other, Generic)
|
||||||
attr in annotations and
|
and getattr(other, "_is_protocol", False)
|
||||||
issubclass(other, Generic) and getattr(other, '_is_protocol', False)):
|
and attr in _lazy_annotationlib.get_annotations(
|
||||||
|
base, format=_lazy_annotationlib.Format.FORWARDREF
|
||||||
|
)
|
||||||
|
):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
:class:`typing.Protocol` now uses :func:`annotationlib.get_annotations` when
|
||||||
|
checking whether or not an instance implements the protocol with
|
||||||
|
:func:`isinstance`. This enables support for ``isinstance`` checks against
|
||||||
|
classes with deferred annotations.
|
Loading…
Add table
Add a link
Reference in a new issue