Skip to content

Commit bbbdeeb

Browse files
gh-89132: Fix subclassing of annotated types
__mro_entries__() of an annotated type now delegates to __mro_entries__() of the annotated type itself if it is not a class, e.g. a generic alias. Subclassing Annotated[X, ...] is now the same as subclassing X.
1 parent 4ccb600 commit bbbdeeb

3 files changed

Lines changed: 42 additions & 1 deletion

File tree

Lib/test/test_typing.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9943,6 +9943,36 @@ def test_cannot_subclass(self):
99439943
class C(Annotated):
99449944
pass
99459945

9946+
def test_subclass(self):
9947+
# gh-89132: subclassing an annotated type is the same as subclassing
9948+
# the annotated type itself.
9949+
class MyGeneric(Generic[T]):
9950+
pass
9951+
9952+
for tp in (list, List, List[int], list[int], MyGeneric[int],
9953+
collections.abc.Sequence[int]):
9954+
with self.subTest(tp=tp):
9955+
class C(Annotated[tp, "a decoration"]):
9956+
pass
9957+
9958+
class D(tp):
9959+
pass
9960+
9961+
self.assertEqual(C.__bases__, D.__bases__)
9962+
self.assertEqual(C.__mro__[1:], D.__mro__[1:])
9963+
9964+
def test_cannot_subclass_not_subclassable(self):
9965+
# gh-89132: the error message is the same as for the annotated type.
9966+
for tp in (Union[int, str], int | str, T):
9967+
with self.subTest(tp=tp):
9968+
with self.assertRaises(TypeError) as cm:
9969+
class D(tp):
9970+
pass
9971+
with self.assertRaises(TypeError) as cm2:
9972+
class C(Annotated[tp, "a decoration"]):
9973+
pass
9974+
self.assertEqual(str(cm2.exception), str(cm.exception))
9975+
99469976
def test_cannot_check_instance(self):
99479977
with self.assertRaises(TypeError):
99489978
isinstance(5, Annotated[int, "positive"])

Lib/typing.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2254,7 +2254,14 @@ def __getattr__(self, attr):
22542254
return super().__getattr__(attr)
22552255

22562256
def __mro_entries__(self, bases):
2257-
return (self.__origin__,)
2257+
origin = self.__origin__
2258+
if not isinstance(origin, type):
2259+
# The origin can need a resolution itself, e.g. list[int].
2260+
meth = getattr(origin, '__mro_entries__', None)
2261+
if meth is not None:
2262+
bases = tuple(origin if b is self else b for b in bases)
2263+
return meth(bases)
2264+
return (origin,)
22582265

22592266

22602267
@_TypedCacheSpecialForm
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Subclassing :data:`typing.Annotated` types now works if the annotated type
2+
is a generic alias, e.g. ``Annotated[list[int], "metadata"]``. If the
3+
annotated type cannot be subclassed, the raised error is now the same as for
4+
that type.

0 commit comments

Comments
 (0)