summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/_collections_abc.py5
-rw-r--r--Lib/test/test_typing.py55
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst3
-rw-r--r--Objects/genericaliasobject.c8
4 files changed, 71 insertions, 0 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 40417dc..72fd633 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -441,6 +441,8 @@ class _CallableGenericAlias(GenericAlias):
def __parameters__(self):
params = []
for arg in self.__args__:
+ if isinstance(arg, type) and not isinstance(arg, GenericAlias):
+ continue
# Looks like a genericalias
if hasattr(arg, "__parameters__") and isinstance(arg.__parameters__, tuple):
params.extend(arg.__parameters__)
@@ -486,6 +488,9 @@ class _CallableGenericAlias(GenericAlias):
subst = dict(zip(self.__parameters__, item))
new_args = []
for arg in self.__args__:
+ if isinstance(arg, type) and not isinstance(arg, GenericAlias):
+ new_args.append(arg)
+ continue
if _is_typevarlike(arg):
if _is_param_expr(arg):
arg = subst[arg]
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 34f9444..6c53154 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -2498,6 +2498,61 @@ 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 (int, TypeVar, ParamSpec, types.GenericAlias, types.UnionType):
+ with self.subTest(generic=a, sub=b):
+ with self.assertRaisesRegex(TypeError,
+ '.* is not a generic class|'
+ 'no type variables left'):
+ a[b][str]
+ # Duck-typing anything that looks like it has __parameters__.
+ # C version of GenericAlias
+ self.assertEqual(list[A()].__parameters__, (T,))
+
+ def test_non_generic_subscript(self):
+ T = TypeVar('T')
+ class G(Generic[T]):
+ pass
+
+ for s in (int, G, List, list,
+ TypeVar, ParamSpec,
+ types.GenericAlias, types.UnionType):
+
+ for t in Tuple, tuple:
+ with self.subTest(tuple=t, sub=s):
+ self.assertEqual(t[s, T][int], t[s, int])
+ self.assertEqual(t[T, s][int], t[int, s])
+ a = t[s]
+ with self.assertRaises(TypeError):
+ a[int]
+
+ for c in Callable, collections.abc.Callable:
+ with self.subTest(callable=c, sub=s):
+ self.assertEqual(c[[s], T][int], c[[s], int])
+ self.assertEqual(c[[T], s][int], c[[int], s])
+ a = c[[s], s]
+ with self.assertRaises(TypeError):
+ a[int]
+
+
class ClassVarTests(BaseTestCase):
def test_basics(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst
new file mode 100644
index 0000000..25c4737
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-31-21-01-35.gh-issue-98852.MYaRN6.rst
@@ -0,0 +1,3 @@
+Fix subscription of :class:`types.GenericAlias` instances containing bare
+generic types: for example ``tuple[A, T][int]``,
+where ``A`` is a generic type, and ``T`` is a type variable.
diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c
index f52bc97..9edb6d2 100644
--- a/Objects/genericaliasobject.c
+++ b/Objects/genericaliasobject.c
@@ -209,6 +209,9 @@ _Py_make_parameters(PyObject *args)
Py_ssize_t iparam = 0;
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *t = PyTuple_GET_ITEM(args, iarg);
+ if (PyType_Check(t)) {
+ continue;
+ }
int typevar = is_typevar(t);
if (typevar < 0) {
Py_DECREF(parameters);
@@ -260,6 +263,11 @@ _Py_make_parameters(PyObject *args)
static PyObject *
subs_tvars(PyObject *obj, PyObject *params, PyObject **argitems)
{
+ if (PyType_Check(obj)) {
+ Py_INCREF(obj);
+ return obj;
+ }
+
_Py_IDENTIFIER(__parameters__);
PyObject *subparams;
if (_PyObject_LookupAttrId(obj, &PyId___parameters__, &subparams) < 0) {