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]

Reply via email to