From 57aae6249dd3c068a7068fd11e7d67379a544acf Mon Sep 17 00:00:00 2001 From: Maxim Martynov Date: Wed, 24 May 2023 13:42:58 +0300 Subject: [PATCH] Fix isinstance check for Generic classes on Python 3.7 (#188) --- CHANGELOG.md | 6 ++++++ src/test_typing_extensions.py | 23 +++++++++++++++++++++++ src/typing_extensions.py | 5 +++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b163a93..853c211c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# Unreleased + +- Fix regression in version 4.6.1 where comparing a generic class against a + runtime-checkable protocol using `isinstance()` would cause `AttributeError` + to be raised if using Python 3.7 + # Release 4.6.1 (May 23, 2023) - Change deprecated `@runtime` to formal API `@runtime_checkable` in the error diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 9c605fa4..736b46b4 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -2246,6 +2246,28 @@ class Foo: ... del f.x self.assertNotIsInstance(f, HasX) + def test_protocols_isinstance_generic_classes(self): + T = TypeVar("T") + + class Foo(Generic[T]): + x: T + + def __init__(self, x): + self.x = x + + class Bar(Foo[int]): + ... + + @runtime_checkable + class HasX(Protocol): + x: int + + foo = Foo(1) + self.assertIsInstance(foo, HasX) + + bar = Bar(2) + self.assertIsInstance(bar, HasX) + def test_protocols_support_register(self): @runtime_checkable class P(Protocol): @@ -4330,6 +4352,7 @@ class Y(Generic[T], NamedTuple): a = A(3) self.assertIs(type(a), G) + self.assertIsInstance(a, G) self.assertEqual(a.x, 3) things = "arguments" if sys.version_info >= (3, 11) else "parameters" diff --git a/src/typing_extensions.py b/src/typing_extensions.py index f13859f0..a3f45daa 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -9,7 +9,6 @@ import typing import warnings - __all__ = [ # Super-special typing primitives. 'Any', @@ -659,7 +658,9 @@ def _proto_hook(cls, other): isinstance(annotations, collections.abc.Mapping) and attr in annotations and issubclass(other, (typing.Generic, _ProtocolMeta)) - and other._is_protocol + # All subclasses of Generic have an _is_proto attribute on 3.8+ + # But not on 3.7 + and getattr(other, "_is_protocol", False) ): break else: