Skip to content

Commit b4e232c

Browse files
gh-94607: Fix subclassing generics (GH-94610)
Co-authored-by: Serhiy Storchaka <[email protected]> (cherry picked from commit 6442a9d) Co-authored-by: Ken Jin <[email protected]>
1 parent cb4359c commit b4e232c

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

Lib/test/test_typing.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3644,6 +3644,35 @@ def test_subclass_special_form(self):
36443644
class Foo(obj):
36453645
pass
36463646

3647+
def test_complex_subclasses(self):
3648+
T_co = TypeVar("T_co", covariant=True)
3649+
3650+
class Base(Generic[T_co]):
3651+
...
3652+
3653+
T = TypeVar("T")
3654+
3655+
# see gh-94607: this fails in that bug
3656+
class Sub(Base, Generic[T]):
3657+
...
3658+
3659+
def test_parameter_detection(self):
3660+
self.assertEqual(List[T].__parameters__, (T,))
3661+
self.assertEqual(List[List[T]].__parameters__, (T,))
3662+
class A:
3663+
__parameters__ = (T,)
3664+
# Bare classes should be skipped
3665+
for a in (List, list):
3666+
for b in (A, int, TypeVar, TypeVarTuple, ParamSpec, types.GenericAlias, types.UnionType):
3667+
with self.subTest(generic=a, sub=b):
3668+
with self.assertRaisesRegex(TypeError, '.* is not a generic class'):
3669+
a[b][str]
3670+
# Duck-typing anything that looks like it has __parameters__.
3671+
# These tests are optional and failure is okay.
3672+
self.assertEqual(List[A()].__parameters__, (T,))
3673+
# C version of GenericAlias
3674+
self.assertEqual(list[A()].__parameters__, (T,))
3675+
36473676
class ClassVarTests(BaseTestCase):
36483677

36493678
def test_basics(self):

Lib/typing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ def _collect_parameters(args):
250250
"""
251251
parameters = []
252252
for t in args:
253+
# We don't want __parameters__ descriptor of a bare Python class.
254+
if isinstance(t, type):
255+
continue
253256
if hasattr(t, '__typing_subst__'):
254257
if t not in parameters:
255258
parameters.append(t)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix subclassing complex generics with type variables in :mod:`typing`. Previously an error message saying ``Some type variables ... are not listed in Generic[...]`` was shown.
2+
:mod:`typing` no longer populates ``__parameters__`` with the ``__parameters__`` of a Python class.

Objects/genericaliasobject.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ _Py_make_parameters(PyObject *args)
219219
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
220220
PyObject *t = PyTuple_GET_ITEM(args, iarg);
221221
PyObject *subst;
222+
// We don't want __parameters__ descriptor of a bare Python class.
223+
if (PyType_Check(t)) {
224+
continue;
225+
}
222226
if (_PyObject_LookupAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) {
223227
Py_DECREF(parameters);
224228
return NULL;

0 commit comments

Comments
 (0)