diff --git a/CHANGELOG.md b/CHANGELOG.md index 733505a5..4f40c3d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Unreleased +- Update error message when attempting to subclass `typing_extensions.TypeVarTuple` + to align with the message produced by `typing` in Python 3.12+. + Patch by Jan-Eric Nitschke. - Fix incorrect behaviour on Python 3.9 and Python 3.10 that meant that calling `isinstance` with `typing_extensions.Concatenate[...]` or `typing_extensions.Unpack[...]` as the first argument could have a different diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 88fa699e..8c2fcc2d 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -110,6 +110,10 @@ KT = TypeVar("KT") VT = TypeVar("VT") +CANNOT_SUBCLASS_TYPE = 'Cannot subclass special typing classes' +NOT_A_BASE_TYPE = r"type '(?:typing|typing_extensions).%s' is not an acceptable base type" +CANNOT_SUBCLASS_INSTANCE = 'Cannot subclass an instance of %s' + # Flags used to mark tests that only apply after a specific # version of the typing module. TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0) @@ -6792,6 +6796,26 @@ def test_pickle(self): self.assertEqual(z.__name__, typevartuple.__name__) self.assertEqual(z.__default__, typevartuple.__default__) + def test_cannot_subclass(self): + with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVarTuple'): + class C(TypeVarTuple): pass + Ts = TypeVarTuple('Ts') + with self.assertRaisesRegex(TypeError, + CANNOT_SUBCLASS_INSTANCE % 'TypeVarTuple'): + class D(Ts): pass + with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): + class E(type(Unpack)): pass + with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): + class F(type(*Ts)): pass + with self.assertRaisesRegex(TypeError, CANNOT_SUBCLASS_TYPE): + class G(type(Unpack[Ts])): pass + with self.assertRaises(TypeError): + class H(Unpack): pass + with self.assertRaises(TypeError): + class I(*Ts): pass # noqa: E742 + with self.assertRaises(TypeError): + class J(Unpack[Ts]): pass + class FinalDecoratorTests(BaseTestCase): def test_final_unmodified(self): diff --git a/src/typing_extensions.py b/src/typing_extensions.py index bd67a80a..c26f96fc 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -2634,10 +2634,18 @@ def _typevartuple_prepare_subst(alias, args): ) tvt.__typing_prepare_subst__ = _typevartuple_prepare_subst + + def __mro_entries__(bases): + raise TypeError("Cannot subclass an instance of TypeVarTuple.") + tvt.__mro_entries__ = __mro_entries__ + return tvt def __init_subclass__(self, *args, **kwds): - raise TypeError("Cannot subclass special typing classes") + raise TypeError( + f"type '{__name__}.TypeVarTuple' is not an acceptable base type" + ) + else: # <=3.10 class TypeVarTuple(_DefaultMixin): @@ -2715,7 +2723,12 @@ def __reduce__(self): def __init_subclass__(self, *args, **kwds): if '_root' not in kwds: - raise TypeError("Cannot subclass special typing classes") + raise TypeError( + f"type '{__name__}.TypeVarTuple' is not an acceptable base type" + ) + + def __mro_entries__(self, bases): + raise TypeError("Cannot subclass an instance of TypeVarTuple.") if hasattr(typing, "reveal_type"): # 3.11+