https://github.com/python/cpython/commit/e13ecaeca2da14d81a9a2597bdbbc3f4552433ab
commit: e13ecaeca2da14d81a9a2597bdbbc3f4552433ab
branch: main
author: cui fliter <[email protected]>
committer: iritkatriel <[email protected]>
date: 2026-09-03T13:06:35Z
summary:
gh-155141: Improve stack-use estimation for keyword calls (#155785)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-08-14-19-00-00.gh-issue-155141.call-kw.rst
M Lib/test/test_compile.py
M Python/codegen.c
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index df473d59fff3d8..959732fc6e4a83 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -63,6 +63,30 @@ def test_argument_handling(self):
self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass')
self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
+ def test_call_opcode_stack_use_limit(self):
+ def get_call_opcode(positional_count, keyword_count):
+ args = ["0"] * positional_count
+ args.extend(f"a{i}=0" for i in range(keyword_count))
+ code = compile(f"f({', '.join(args)})", "<test>", "exec")
+ return next(
+ instr.opname for instr in dis.get_instructions(code)
+ if instr.opname.startswith("CALL")
+ )
+
+ for positional_count, keyword_count, expected_opcode in [
+ (0, 16, "CALL_KW"),
+ (15, 14, "CALL_KW"),
+ (15, 15, "CALL_FUNCTION_EX"),
+ ]:
+ with self.subTest(
+ positional_count=positional_count,
+ keyword_count=keyword_count,
+ ):
+ self.assertEqual(
+ get_call_opcode(positional_count, keyword_count),
+ expected_opcode,
+ )
+
def test_syntax_error(self):
self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-14-19-00-00.gh-issue-155141.call-kw.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-14-19-00-00.gh-issue-155141.call-kw.rst
new file mode 100644
index 00000000000000..caee6e10168eab
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-14-19-00-00.gh-issue-155141.call-kw.rst
@@ -0,0 +1,3 @@
+Compile pure-keyword calls with 16 to 29 keyword arguments using the faster
+``CALL_KW`` instruction. This includes common cases such as dataclass
+constructors with many fields.
diff --git a/Python/codegen.c b/Python/codegen.c
index 79b84f13e629c7..e2ef40b4e30490 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -87,6 +87,9 @@ typedef _PyCompile_FBlockInfo fblockinfo;
#define LOC(x) SRC_LOCATION_FROM_AST(x)
+#define CALL_STACK_USE(nargs, nkwds) \
+ ((nargs) + (nkwds) + ((nkwds) != 0))
+
#define NEW_JUMP_TARGET_LABEL(C, NAME) \
jump_target_label NAME =
_PyInstructionSequence_NewLabel(INSTR_SEQUENCE(C)); \
if (!IS_JUMP_TARGET_LABEL(NAME)) { \
@@ -4147,7 +4150,7 @@ maybe_optimize_method_call(compiler *c, expr_ty e)
/* Check that there aren't too many arguments */
argsl = asdl_seq_LEN(args);
kwdsl = asdl_seq_LEN(kwds);
- if (argsl + kwdsl + (kwdsl != 0) >= _PY_STACK_USE_GUIDELINE) {
+ if (CALL_STACK_USE(argsl, kwdsl) >= _PY_STACK_USE_GUIDELINE) {
return 0;
}
/* Check that there are no *varargs types of arguments. */
@@ -4440,7 +4443,7 @@ codegen_call_helper_impl(compiler *c, location loc,
nelts = asdl_seq_LEN(args);
nkwelts = asdl_seq_LEN(keywords);
- if (nelts + nkwelts*2 > _PY_STACK_USE_GUIDELINE) {
+ if (CALL_STACK_USE(nelts, nkwelts) > _PY_STACK_USE_GUIDELINE) {
goto ex_call;
}
for (i = 0; i < nelts; i++) {
_______________________________________________
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]