https://github.com/python/cpython/commit/7782794850f1ba7f100ca65daf4caa083a3b5101
commit: 7782794850f1ba7f100ca65daf4caa083a3b5101
branch: main
author: Timofei <[email protected]>
committer: JelleZijlstra <[email protected]>
date: 2026-06-21T15:08:01-07:00
summary:
gh-151665: Fix inspect.signature() on type alias and type parameter evaluators
(#151837)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-06-21-12-55-18.gh-issue-151665.82fmzx.rst
M Lib/test/test_type_params.py
M Python/codegen.c
diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py
index 84c1b9541367363..b6588269e1d0c65 100644
--- a/Lib/test/test_type_params.py
+++ b/Lib/test/test_type_params.py
@@ -1,4 +1,5 @@
import annotationlib
+import inspect
import textwrap
import types
import unittest
@@ -1446,6 +1447,30 @@ def f[T: int = int, **P = int, *Ts = int](): pass
self.assertIs(annotationlib.call_evaluate_function(case,
annotationlib.Format.FORWARDREF), int)
self.assertEqual(annotationlib.call_evaluate_function(case,
annotationlib.Format.STRING), 'int')
+ def test_signature(self):
+ # gh-151665: the ".format" parameter of compiler-generated evaluators
+ # used to break inspect.signature(). It should show up as "format".
+ type Alias = int
+ def f[T: int = int, **P = int, *Ts = int](): pass
+ T, P, Ts = f.__type_params__
+ def g[U: (int, str)](): pass
+ U, = g.__type_params__
+ cases = [
+ Alias.evaluate_value,
+ T.evaluate_bound,
+ T.evaluate_default,
+ P.evaluate_default,
+ Ts.evaluate_default,
+ U.evaluate_constraints,
+ ]
+ for case in cases:
+ with self.subTest(case=case):
+ sig = inspect.signature(case)
+ self.assertEqual(str(sig), '(format=1, /)')
+ param, = sig.parameters.values()
+ self.assertEqual(param.name, 'format')
+ self.assertIs(param.kind, inspect.Parameter.POSITIONAL_ONLY)
+
def test_constraints(self):
def f[T: (int, str)](): pass
T, = f.__type_params__
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-21-12-55-18.gh-issue-151665.82fmzx.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-21-12-55-18.gh-issue-151665.82fmzx.rst
new file mode 100644
index 000000000000000..d08a1220cbe5efc
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-21-12-55-18.gh-issue-151665.82fmzx.rst
@@ -0,0 +1,2 @@
+:func:`inspect.signature` now works on the lazy evaluators of type aliases
+and type parameters instead of raising :exc:`ValueError`.
diff --git a/Python/codegen.c b/Python/codegen.c
index 205c49cff1827c4..e65c308617df5ee 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -733,14 +733,8 @@ codegen_setup_annotations_scope(compiler *c, location loc,
}
static int
-codegen_leave_annotations_scope(compiler *c, location loc)
+codegen_rename_annotations_format_param(PyCodeObject *co)
{
- ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
- PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
- if (co == NULL) {
- return ERROR;
- }
-
// We want the parameter to __annotate__ to be named "format" in the
// signature shown by inspect.signature(), but we need to use a
// different name (.format) in the symtable; if the name
@@ -749,19 +743,16 @@ codegen_leave_annotations_scope(compiler *c, location loc)
// co->co_localsplusnames = ("format", *co->co_localsplusnames[1:])
const Py_ssize_t size = PyObject_Size(co->co_localsplusnames);
if (size == -1) {
- Py_DECREF(co);
return ERROR;
}
PyObject *new_names = PyTuple_New(size);
if (new_names == NULL) {
- Py_DECREF(co);
return ERROR;
}
PyTuple_SET_ITEM(new_names, 0, Py_NewRef(&_Py_ID(format)));
for (int i = 1; i < size; i++) {
PyObject *item = PyTuple_GetItem(co->co_localsplusnames, i);
if (item == NULL) {
- Py_DECREF(co);
Py_DECREF(new_names);
return ERROR;
}
@@ -769,6 +760,22 @@ codegen_leave_annotations_scope(compiler *c, location loc)
PyTuple_SET_ITEM(new_names, i, item);
}
Py_SETREF(co->co_localsplusnames, new_names);
+ return SUCCESS;
+}
+
+static int
+codegen_leave_annotations_scope(compiler *c, location loc)
+{
+ ADDOP_IN_SCOPE(c, loc, RETURN_VALUE);
+ PyCodeObject *co = _PyCompile_OptimizeAndAssemble(c, 1);
+ if (co == NULL) {
+ return ERROR;
+ }
+
+ if (codegen_rename_annotations_format_param(co) < 0) {
+ Py_DECREF(co);
+ return ERROR;
+ }
_PyCompile_ExitScope(c);
int ret = codegen_make_closure(c, loc, co, 0);
@@ -1269,6 +1276,10 @@ codegen_type_param_bound_or_default(compiler *c, expr_ty
e,
if (co == NULL) {
return ERROR;
}
+ if (codegen_rename_annotations_format_param(co) < 0) {
+ Py_DECREF(co);
+ return ERROR;
+ }
int ret = codegen_make_closure(c, LOC(e), co, MAKE_FUNCTION_DEFAULTS);
Py_DECREF(co);
RETURN_IF_ERROR(ret);
@@ -1770,6 +1781,10 @@ codegen_typealias_body(compiler *c, stmt_ty s)
if (co == NULL) {
return ERROR;
}
+ if (codegen_rename_annotations_format_param(co) < 0) {
+ Py_DECREF(co);
+ return ERROR;
+ }
int ret = codegen_make_closure(c, loc, co, MAKE_FUNCTION_DEFAULTS);
Py_DECREF(co);
RETURN_IF_ERROR(ret);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]