gh-94607: Fix subclassing generics (GH-94610)

Co-authored-by: Serhiy Storchaka <3659035+serhiy-storchaka@users.noreply.github.com>
This commit is contained in:
Ken Jin 2022-07-09 12:18:01 +08:00 committed by GitHub
parent 8a285df806
commit 6442a9dd21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 0 deletions

View File

@ -3650,6 +3650,35 @@ class GenericTests(BaseTestCase):
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):

View File

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

View File

@ -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.

View File

@ -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;