Skip to content
Merged
17 changes: 16 additions & 1 deletion 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_isinstance_against_superproto_doesnt_affect_subproto_instance(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 @@ -6323,7 +6338,7 @@ def test_lazy_import(self):
"inspect",
"re",
"contextlib",
# "annotationlib", # TODO
"annotationlib",
})


Expand Down
29 changes: 18 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,17 @@ 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):
# We avoid the slower path through annotationlib here because in most
# cases it should be unnecessary.
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