Skip to content

Commit 6442a9d

Browse files
gh-94607: Fix subclassing generics (GH-94610)
Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 8a285df commit 6442a9d

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
@@ -3650,6 +3650,35 @@ def test_subclass_special_form(self):
36503650
class Foo(obj):
36513651
pass
36523652

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

36553684
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)