Skip to content

Commit bbf1979

Browse files
tomasr8JelleZijlstraAlexWaygood
authored
gh-124445: Allow specializing generic ParamSpec aliases (#124512)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 3a7f17c commit bbf1979

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

‎Lib/test/test_genericalias.py‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,76 @@ def test_del_iter(self):
474474
iter_x=iter(t)
475475
deliter_x
476476

477+
deftest_paramspec_specialization(self):
478+
# gh-124445
479+
T=TypeVar("T")
480+
U=TypeVar("U")
481+
type X[**P] =Callable[P, int]
482+
483+
generic=X[[T]]
484+
self.assertEqual(generic.__args__, ([T],))
485+
self.assertEqual(generic.__parameters__, (T,))
486+
specialized=generic[str]
487+
self.assertEqual(specialized.__args__, ([str],))
488+
self.assertEqual(specialized.__parameters__, ())
489+
490+
generic=X[(T,)]
491+
self.assertEqual(generic.__args__, (T,))
492+
self.assertEqual(generic.__parameters__, (T,))
493+
specialized=generic[str]
494+
self.assertEqual(specialized.__args__, (str,))
495+
self.assertEqual(specialized.__parameters__, ())
496+
497+
generic=X[[T, U]]
498+
self.assertEqual(generic.__args__, ([T, U],))
499+
self.assertEqual(generic.__parameters__, (T, U))
500+
specialized=generic[str, int]
501+
self.assertEqual(specialized.__args__, ([str, int],))
502+
self.assertEqual(specialized.__parameters__, ())
503+
504+
generic=X[(T, U)]
505+
self.assertEqual(generic.__args__, (T, U))
506+
self.assertEqual(generic.__parameters__, (T, U))
507+
specialized=generic[str, int]
508+
self.assertEqual(specialized.__args__, (str, int))
509+
self.assertEqual(specialized.__parameters__, ())
510+
511+
deftest_nested_paramspec_specialization(self):
512+
# gh-124445
513+
type X[**P, T] =Callable[P, T]
514+
515+
x_list=X[[int, str], float]
516+
self.assertEqual(x_list.__args__, ([int, str], float))
517+
self.assertEqual(x_list.__parameters__, ())
518+
519+
x_tuple=X[(int, str), float]
520+
self.assertEqual(x_tuple.__args__, ((int, str), float))
521+
self.assertEqual(x_tuple.__parameters__, ())
522+
523+
U=TypeVar("U")
524+
V=TypeVar("V")
525+
526+
multiple_params_list=X[[int, U], V]
527+
self.assertEqual(multiple_params_list.__args__, ([int, U], V))
528+
self.assertEqual(multiple_params_list.__parameters__, (U, V))
529+
multiple_params_list_specialized=multiple_params_list[str, float]
530+
self.assertEqual(multiple_params_list_specialized.__args__, ([int, str], float))
531+
self.assertEqual(multiple_params_list_specialized.__parameters__, ())
532+
533+
multiple_params_tuple=X[(int, U), V]
534+
self.assertEqual(multiple_params_tuple.__args__, ((int, U), V))
535+
self.assertEqual(multiple_params_tuple.__parameters__, (U, V))
536+
multiple_params_tuple_specialized=multiple_params_tuple[str, float]
537+
self.assertEqual(multiple_params_tuple_specialized.__args__, ((int, str), float))
538+
self.assertEqual(multiple_params_tuple_specialized.__parameters__, ())
539+
540+
deeply_nested=X[[U, [V], int], V]
541+
self.assertEqual(deeply_nested.__args__, ([U, [V], int], V))
542+
self.assertEqual(deeply_nested.__parameters__, (U, V))
543+
deeply_nested_specialized=deeply_nested[str, float]
544+
self.assertEqual(deeply_nested_specialized.__args__, ([str, [float], int], float))
545+
self.assertEqual(deeply_nested_specialized.__parameters__, ())
546+
477547

478548
classTypeIterationTests(unittest.TestCase):
479549
_UNITERABLE_TYPES= (list, tuple)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix specialization of generic aliases that are generic over a
2+
:class:`typing.ParamSpec` and have been specialized with a
3+
nested type variable.

‎Objects/genericaliasobject.c‎

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,22 @@ tuple_extend(PyObject **dst, Py_ssize_t dstindex,
180180
PyObject*
181181
_Py_make_parameters(PyObject*args)
182182
{
183+
assert(PyTuple_Check(args) ||PyList_Check(args));
184+
constboolis_args_list=PyList_Check(args);
185+
PyObject*tuple_args=NULL;
186+
if (is_args_list){
187+
args=tuple_args=PySequence_Tuple(args);
188+
if (args==NULL){
189+
returnNULL;
190+
}
191+
}
183192
Py_ssize_tnargs=PyTuple_GET_SIZE(args);
184193
Py_ssize_tlen=nargs;
185194
PyObject*parameters=PyTuple_New(len);
186-
if (parameters==NULL)
195+
if (parameters==NULL){
196+
Py_XDECREF(tuple_args);
187197
returnNULL;
198+
}
188199
Py_ssize_tiparam=0;
189200
for (Py_ssize_tiarg=0; iarg<nargs; iarg++){
190201
PyObject*t=PyTuple_GET_ITEM(args, iarg);
@@ -195,6 +206,7 @@ _Py_make_parameters(PyObject *args)
195206
intrc=PyObject_HasAttrWithError(t, &_Py_ID(__typing_subst__));
196207
if (rc<0){
197208
Py_DECREF(parameters);
209+
Py_XDECREF(tuple_args);
198210
returnNULL;
199211
}
200212
if (rc){
@@ -205,8 +217,19 @@ _Py_make_parameters(PyObject *args)
205217
if (PyObject_GetOptionalAttr(t, &_Py_ID(__parameters__),
206218
&subparams) <0){
207219
Py_DECREF(parameters);
220+
Py_XDECREF(tuple_args);
208221
returnNULL;
209222
}
223+
if (!subparams&& (PyTuple_Check(t) ||PyList_Check(t))){
224+
// Recursively call _Py_make_parameters for lists/tuples and
225+
// add the results to the current parameters.
226+
subparams=_Py_make_parameters(t);
227+
if (subparams==NULL){
228+
Py_DECREF(parameters);
229+
Py_XDECREF(tuple_args);
230+
returnNULL;
231+
}
232+
}
210233
if (subparams&&PyTuple_Check(subparams)){
211234
Py_ssize_tlen2=PyTuple_GET_SIZE(subparams);
212235
Py_ssize_tneeded=len2-1- (iarg-iparam);
@@ -215,6 +238,7 @@ _Py_make_parameters(PyObject *args)
215238
if (_PyTuple_Resize(&parameters, len) <0){
216239
Py_DECREF(subparams);
217240
Py_DECREF(parameters);
241+
Py_XDECREF(tuple_args);
218242
returnNULL;
219243
}
220244
}
@@ -229,9 +253,11 @@ _Py_make_parameters(PyObject *args)
229253
if (iparam<len){
230254
if (_PyTuple_Resize(&parameters, iparam) <0){
231255
Py_XDECREF(parameters);
256+
Py_XDECREF(tuple_args);
232257
returnNULL;
233258
}
234259
}
260+
Py_XDECREF(tuple_args);
235261
returnparameters;
236262
}
237263

@@ -416,11 +442,22 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
416442
t = list[T]; t[int] -> newargs = [int]
417443
t = dict[str, T]; t[int] -> newargs = [str, int]
418444
t = dict[T, list[S]]; t[str, int] -> newargs = [str, list[int]]
445+
t = list[[T]]; t[str] -> newargs = [[str]]
419446
*/
447+
assert (PyTuple_Check(args) ||PyList_Check(args));
448+
constboolis_args_list=PyList_Check(args);
449+
PyObject*tuple_args=NULL;
450+
if (is_args_list){
451+
args=tuple_args=PySequence_Tuple(args);
452+
if (args==NULL){
453+
returnNULL;
454+
}
455+
}
420456
Py_ssize_tnargs=PyTuple_GET_SIZE(args);
421457
PyObject*newargs=PyTuple_New(nargs);
422458
if (newargs==NULL){
423459
Py_DECREF(item);
460+
Py_XDECREF(tuple_args);
424461
returnNULL;
425462
}
426463
for (Py_ssize_tiarg=0, jarg=0; iarg<nargs; iarg++){
@@ -430,17 +467,46 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
430467
jarg++;
431468
continue;
432469
}
433-
470+
// Recursively substitute params in lists/tuples.
471+
if (PyTuple_Check(arg) ||PyList_Check(arg)){
472+
PyObject*subargs=_Py_subs_parameters(self, arg, parameters, item);
473+
if (subargs==NULL){
474+
Py_DECREF(newargs);
475+
Py_DECREF(item);
476+
Py_XDECREF(tuple_args);
477+
returnNULL;
478+
}
479+
if (PyTuple_Check(arg)){
480+
PyTuple_SET_ITEM(newargs, jarg, subargs);
481+
}
482+
else{
483+
// _Py_subs_parameters returns a tuple. If the original arg was a list,
484+
// convert subargs to a list as well.
485+
PyObject*subargs_list=PySequence_List(subargs);
486+
Py_DECREF(subargs);
487+
if (subargs_list==NULL){
488+
Py_DECREF(newargs);
489+
Py_DECREF(item);
490+
Py_XDECREF(tuple_args);
491+
returnNULL;
492+
}
493+
PyTuple_SET_ITEM(newargs, jarg, subargs_list);
494+
}
495+
jarg++;
496+
continue;
497+
}
434498
intunpack=_is_unpacked_typevartuple(arg);
435499
if (unpack<0){
436500
Py_DECREF(newargs);
437501
Py_DECREF(item);
502+
Py_XDECREF(tuple_args);
438503
returnNULL;
439504
}
440505
PyObject*subst;
441506
if (PyObject_GetOptionalAttr(arg, &_Py_ID(__typing_subst__), &subst) <0){
442507
Py_DECREF(newargs);
443508
Py_DECREF(item);
509+
Py_XDECREF(tuple_args);
444510
returnNULL;
445511
}
446512
if (subst){
@@ -455,6 +521,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
455521
if (arg==NULL){
456522
Py_DECREF(newargs);
457523
Py_DECREF(item);
524+
Py_XDECREF(tuple_args);
458525
returnNULL;
459526
}
460527
if (unpack){
@@ -463,6 +530,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
463530
Py_DECREF(arg);
464531
if (jarg<0){
465532
Py_DECREF(item);
533+
Py_XDECREF(tuple_args);
466534
returnNULL;
467535
}
468536
}
@@ -473,6 +541,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
473541
}
474542

475543
Py_DECREF(item);
544+
Py_XDECREF(tuple_args);
476545
returnnewargs;
477546
}
478547

0 commit comments

Comments
(0)