10000 Fix TypeVar substitution · python/cpython@12581d8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 12581d8

Browse files
committed
Fix TypeVar substitution
1 parent 7d54ace commit 12581d8

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

Lib/test/test_typing.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,17 @@ class A(Generic[T, Unpack[Ts]]): ...
629629
self.assertEqual(A[float, range].__args__, (float, range))
630630
self.assertEqual(A[float, *tuple[int, ...]].__args__, ( 8000 float, *tuple[int, ...]))
631631

632+
def test_typevar_and_typevartuple_specialization(self):
633+
T = TypeVar("T")
634+
U = TypeVar("U", default=float)
635+
Ts = TypeVarTuple('Ts', default=Unpack[Tuple[str, int]])
636+
self.assertEqual(Ts.__default__, Unpack[Tuple[str, int]])
637+
class A(Generic[T, U, Unpack[Ts]]): ...
638+
self.assertEqual(A[int].__args__, (int, float, str, int))
639+
self.assertEqual(A[int, str].__args__, (int, str, str, int))
640+
self.assertEqual(A[int, str, range].__args__, (int, str, range))
641+
self.assertEqual(A[int, str, *tuple[int, ...]].__args__, (int, str, *tuple[int, ...]))
642+
632643
def test_typevartuple_none(self):
633644
U = TypeVarTuple('U')
634645
U_None = TypeVarTuple('U_None', default=None)

Objects/clinic/typevarobject.c.h

Lines changed: 31 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/typevarobject.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,66 @@ typevar_typing_subst(typevarobject *self, PyObject *arg)
448448
return result;
449449
}
450450

451+
/*[clinic input]
452+
typevar.__typing_prepare_subst__ as typevar_typing_prepare_subst
453+
454+
alias: object
455+
args: object
456+
/
457+
458+
[clinic start generated code]*/
459+
460+
static PyObject *
461+
typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias,
462+
PyObject *args)
463+
/*[clinic end generated code: output=82c3f4691e0ded22 input=201a750415d14ffb]*/
464+
{
465+
PyObject *params = PyObject_GetAttrString(alias, "__parameters__");
466+
if (params == NULL) {
467+
return NULL;
468+
}
469+
Py_ssize_t i = PySequence_Index(params, (PyObject *)self);
470+
if (i == -1) {
471+
Py_DECREF(params);
472+
return NULL;
473+
}
474+
Py_ssize_t args_len = PySequence_Length(args);
475+
if (args_len == -1) {
476+
Py_DECREF(params);
477+
return NULL;
478+
}
479+
if (i < args_len) {
480+
// We already have a value for our TypeVar
481+
Py_DECREF(params);
482+
return Py_NewRef(args);
483+
}
484+
else if (i == args_len) {
485+
// If the TypeVar has a non-None default, use it.
486+
PyObject *dflt = typevar_default(self, NULL);
487+
if (dflt == NULL) {
488< F1BF /td>+
Py_DECREF(params);
489+
return NULL;
490+
}
491+
if (!Py_IsNone(dflt)) {
492+
PyObject *new_args = PyTuple_Pack(1, dflt);
493+
Py_DECREF(dflt);
494+
if (new_args == NULL) {
495+
Py_DECREF(params);
496+
return NULL;
497+
}
498+
PyObject *result = PySequence_Concat(args, new_args);
499+
Py_DECREF(params);
500+
Py_DECREF(new_args);
501+
return result;
502+
}
503+
}
504+
Py_DECREF(params);
505+
PyErr_Format(PyExc_TypeError,
506+
"Too few arguments for %s",
507+
alias);
508+
return NULL;
509+
}
510+
451511
/*[clinic input]
452512
typevar.__reduce__ as typevar_reduce
453513
@@ -470,6 +530,7 @@ typevar_mro_entries(PyObject *self, PyObject *args)
470530

471531
static PyMethodDef typevar_methods[] = {
472532
TYPEVAR_TYPING_SUBST_METHODDEF
533+
TYPEVAR_TYPING_PREPARE_SUBST_METHODDEF
473534
TYPEVAR_REDUCE_METHODDEF
474535
{"__mro_entries__", typevar_mro_entries, METH_O},
475536
{0}

0 commit comments

Comments
 (0)
0