Skip to content

gh-94607: Fix subclassing generics #94610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 9, 2022
29 changes: 29 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
@@ -3650,6 +3650,35 @@ def test_subclass_special_form(self):
class Foo(obj):
pass

def test_complex_subclasses(self):
T_co = TypeVar("T_co", covariant=True)

class Base(Generic[T_co]):
...

T = TypeVar("T")

# see gh-94607: this fails in that bug
class Sub(Base, Generic[T]):
...

def test_parameter_detection(self):
self.assertEqual(List[T].__parameters__, (T,))
self.assertEqual(List[List[T]].__parameters__, (T,))
class A:
__parameters__ = (T,)
# Bare classes should be skipped
for a in (List, list):
for b in (A, int, TypeVar, TypeVarTuple, ParamSpec, types.GenericAlias, types.UnionType):
with self.subTest(generic=a, sub=b):
with self.assertRaisesRegex(TypeError, '.* is not a generic class'):
a[b][str]
# Duck-typing anything that looks like it has __parameters__.
# These tests are optional and failure is okay.
self.assertEqual(List[A()].__parameters__, (T,))
# C version of GenericAlias
self.assertEqual(list[A()].__parameters__, (T,))

class ClassVarTests(BaseTestCase):

def test_basics(self):
3 changes: 3 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
@@ -250,6 +250,9 @@ def _collect_parameters(args):
"""
parameters = []
for t in args:
# We don't want __parameters__ descriptor of a bare Python class.
if isinstance(t, type):
continue
if hasattr(t, '__typing_subst__'):
if t not in parameters:
parameters.append(t)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
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.
:mod:`typing` no longer populates ``__parameters__`` with the ``__parameters__`` of a Python class.
4 changes: 4 additions & 0 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
@@ -219,6 +219,10 @@ _Py_make_parameters(PyObject *args)
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *t = PyTuple_GET_ITEM(args, iarg);
PyObject *subst;
// We don't want __parameters__ descriptor of a bare Python class.
if (PyType_Check(t)) {
continue;
}
if (_PyObject_LookupAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) {
Py_DECREF(parameters);
return NULL;