Skip to content

Commit 7782794

Browse files
authored
gh-151665: Fix inspect.signature() on type alias and type parameter evaluators (#151837)
1 parent 30aeeb3 commit 7782794

3 files changed

Lines changed: 52 additions & 10 deletions

File tree

Lib/test/test_type_params.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import annotationlib
2+
import inspect
23
import textwrap
34
import types
45
import unittest
@@ -1446,6 +1447,30 @@ def f[T: int = int, **P = int, *Ts = int](): pass
14461447
self.assertIs(annotationlib.call_evaluate_function(case, annotationlib.Format.FORWARDREF), int)
14471448
self.assertEqual(annotationlib.call_evaluate_function(case, annotationlib.Format.STRING), 'int')
14481449

1450+
def test_signature(self):
1451+
# gh-151665: the ".format" parameter of compiler-generated evaluators
1452+
# used to break inspect.signature(). It should show up as "format".
1453+
type Alias = int
1454+
def f[T: int = int, **P = int, *Ts = int](): pass
1455+
T, P, Ts = f.__type_params__
1456+
def g[U: (int, str)](): pass
1457+
U, = g.__type_params__
1458+
cases = [
1459+
Alias.evaluate_value,
1460+
T.evaluate_bound,
1461+
T.evaluate_default,
1462+
P.evaluate_default,
1463+
Ts.evaluate_default,
1464+
U.evaluate_constraints,
1465+
]
1466+
for case in cases:
1467+
with self.subTest(case=case):
1468+
sig = inspect.signature(case)
1469+
self.assertEqual(str(sig), '(format=1, /)')
1470+
param, = sig.parameters.values()
1471+
self.assertEqual(param.name, 'format')
1472+
self.assertIs(param.kind, inspect.Parameter.POSITIONAL_ONLY)
1473+
14491474
def test_constraints(self):
14501475
def f[T: (int, str)](): pass
14511476
T, = f.__type_params__
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`inspect.signature` now works on the lazy evaluators of type aliases
2+
and type parameters instead of raising :exc:`ValueError`.

Python/codegen.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -733,14 +733,8 @@ codegen_setup_annotations_scope(compiler *c, location loc,
733733
}
734734

735735
static int
736-
codegen_leave_annotations_scope(compiler *c, location loc)
736+
codegen_rename_annotations_format_param(PyCodeObject *co)
737737
{
738-
ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
739-
PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
740-
if (co == NULL) {
741-
return ERROR;
742-
}
743-
744738
// We want the parameter to __annotate__ to be named "format" in the
745739
// signature shown by inspect.signature(), but we need to use a
746740
// different name (.format) in the symtable; if the name
@@ -749,26 +743,39 @@ codegen_leave_annotations_scope(compiler *c, location loc)
749743
// co->co_localsplusnames = ("format", *co->co_localsplusnames[1:])
750744
const Py_ssize_t size = PyObject_Size(co->co_localsplusnames);
751745
if (size == -1) {
752-
Py_DECREF(co);
753746
return ERROR;
754747
}
755748
PyObject *new_names = PyTuple_New(size);
756749
if (new_names == NULL) {
757-
Py_DECREF(co);
758750
return ERROR;
759751
}
760752
PyTuple_SET_ITEM(new_names, 0, Py_NewRef(&_Py_ID(format)));
761753
for (int i = 1; i < size; i++) {
762754
PyObject *item = PyTuple_GetItem(co->co_localsplusnames, i);
763755
if (item == NULL) {
764-
Py_DECREF(co);
765756
Py_DECREF(new_names);
766757
return ERROR;
767758
}
768759
Py_INCREF(item);
769760
PyTuple_SET_ITEM(new_names, i, item);
770761
}
771762
Py_SETREF(co->co_localsplusnames, new_names);
763+
return SUCCESS;
764+
}
765+
766+
static int
767+
codegen_leave_annotations_scope(compiler *c, location loc)
768+
{
769+
ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
770+
PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
771+
if (co == NULL) {
772+
return ERROR;
773+
}
774+
775+
if (codegen_rename_annotations_format_param(co) < 0) {
776+
Py_DECREF(co);
777+
return ERROR;
778+
}
772779

773780
_PyCompile_ExitScope(c);
774781
int ret = codegen_make_closure(c, loc, co, 0);
@@ -1269,6 +1276,10 @@ codegen_type_param_bound_or_default(compiler *c, expr_ty e,
12691276
if (co == NULL) {
12701277
return ERROR;
12711278
}
1279+
if (codegen_rename_annotations_format_param(co) < 0) {
1280+
Py_DECREF(co);
1281+
return ERROR;
1282+
}
12721283
int ret = codegen_make_closure(c, LOC(e), co, MAKE_FUNCTION_DEFAULTS);
12731284
Py_DECREF(co);
12741285
RETURN_IF_ERROR(ret);
@@ -1770,6 +1781,10 @@ codegen_typealias_body(compiler *c, stmt_ty s)
17701781
if (co == NULL) {
17711782
return ERROR;
17721783
}
1784+
if (codegen_rename_annotations_format_param(co) < 0) {
1785+
Py_DECREF(co);
1786+
return ERROR;
1787+
}
17731788
int ret = codegen_make_closure(c, loc, co, MAKE_FUNCTION_DEFAULTS);
17741789
Py_DECREF(co);
17751790
RETURN_IF_ERROR(ret);

0 commit comments

Comments
 (0)