Skip to content

Commit

Permalink
gh-94607: Fix subclassing generics (GH-94610)
Browse files Browse the repository at this point in the history
Co-authored-by: Serhiy Storchaka <[email protected]>
(cherry picked from commit 6442a9d)

Co-authored-by: Ken Jin <[email protected]>
  • Loading branch information
miss-islington and Fidget-Spinner authored Jul 9, 2022
1 parent cb4359c commit b4e232c
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3644,6 +3644,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):
Expand Down
3 changes: 3 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
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
Expand Up @@ -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;
Expand Down

0 comments on commit b4e232c

Please sign in to comment.