Skip to content
16 changes: 16 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3149,6 +3149,21 @@ def x(self): ...
with self.assertRaisesRegex(TypeError, only_classes_allowed):
issubclass(1, BadPG)

def test_lazy_evaluation_with_subprotocols(self):
@runtime_checkable
class Base(Protocol):
x: int

@runtime_checkable
class Child(Base, Protocol):
y: str

class Capybara:
x = 43

self.assertIsInstance(Capybara(), Base)
self.assertNotIsInstance(Capybara(), Child)

def test_implicit_issubclass_between_two_protocols(self):
@runtime_checkable
class CallableMembersProto(Protocol):
Expand Down Expand Up @@ -3843,6 +3858,7 @@ def meth(self): pass
'_is_protocol', '_is_runtime_protocol', '__parameters__',
'__init__', '__annotations__', '__subclasshook__', '__annotate__',
'__annotations_cache__', '__annotate_func__',
'__protocol_attrs_cache__',
}
self.assertLessEqual(vars(NonP).keys(), vars(C).keys() | acceptable_extra_attrs)
self.assertLessEqual(
Expand Down
27 changes: 16 additions & 11 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1801,9 +1801,13 @@ def _get_protocol_attrs(cls):
for base in cls.__mro__[:-1]: # without object
if base.__name__ in {'Protocol', 'Generic'}:
continue
annotations = _lazy_annotationlib.get_annotations(
base, format=_lazy_annotationlib.Format.FORWARDREF
)
try:
annotations = base.__annotations__
except Exception:
# Only go through annotationlib to handle deferred annotations if we need to
annotations = _lazy_annotationlib.get_annotations(
base, format=_lazy_annotationlib.Format.FORWARDREF
)
for attr in (*base.__dict__, *annotations):
if not attr.startswith('_abc_') and attr not in EXCLUDED_ATTRIBUTES:
attrs.add(attr)
Expand Down Expand Up @@ -2020,14 +2024,15 @@ def _proto_hook(cls, other):
break

# ...or in annotations, if it is a sub-protocol.
if (
issubclass(other, Generic)
and getattr(other, "_is_protocol", False)
and attr in _lazy_annotationlib.get_annotations(
base, format=_lazy_annotationlib.Format.FORWARDREF
)
):
break
if issubclass(other, Generic) and getattr(other, "_is_protocol", False):
try:
annos = base.__annotations__
except Exception:
annos = _lazy_annotationlib.get_annotations(
base, format=_lazy_annotationlib.Format.FORWARDREF
)
if attr in annos:
break
else:
return NotImplemented
return True
Expand Down
Loading