)
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):
class DeferredProto(Protocol):
x: DoesNotExist
break
# ...or in annotations, if it is a sub-protocol.
- annotations = getattr(base, '__annotations__', {})
- if (isinstance(annotations, collections.abc.Mapping) and
- attr in annotations and
- issubclass(other, Generic) and getattr(other, '_is_protocol', False)):
+ if (
+ issubclass(other, Generic)
+ and getattr(other, "_is_protocol", False)
+ and attr in _lazy_annotationlib.get_annotations(
+ base, format=_lazy_annotationlib.Format.FORWARDREF
+ )
+ ):
break
else:
return NotImplemented
--- /dev/null
+: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.