From e13ecaeca2da14d81a9a2597bdbbc3f4552433ab Mon Sep 17 00:00:00 2001 From: cui fliter Date: Thu, 3 Sep 2026 21:06:35 +0800 Subject: [PATCH 1/4] gh-155141: Improve stack-use estimation for keyword calls (#155785) --- Lib/test/test_compile.py | 24 +++++++++++++++++++ ...08-14-19-00-00.gh-issue-155141.call-kw.rst | 3 +++ Python/codegen.c | 7 ++++-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-08-14-19-00-00.gh-issue-155141.call-kw.rst 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)})", "", "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++) { From 4dd225e940001eb96071e56d258e4e6b74b07f8b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 3 Sep 2026 16:17:07 +0200 Subject: [PATCH 2/4] gh-144446: Fix _PyFrame_GetFrameObject() for test_cppext (C++) (#156893) Do an explicit cast to fix the C++ compiler warning: In file included from Include/internal/pycore_object.h:13, from Include/internal/pycore_cell.h:5, from extension.cpp:29: Include/internal/pycore_interpframe.h: In function 'PyFrameObject* _PyFrame_GetFrameObject(_PyInterpreterFrame*)': Include/internal/pycore_pyatomic_ft_wrappers.h:33:32: error: invalid conversion from 'void*' to 'PyFrameObject*' {aka '_frame*'} [-fpermissive] 33 | _Py_atomic_load_ptr_acquire(&value) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~ | | | void* Include/internal/pycore_interpframe.h:348:26: note: in expansion of macro 'FT_ATOMIC_LOAD_PTR_ACQUIRE' 348 | PyFrameObject *res = FT_ATOMIC_LOAD_PTR_ACQUIRE(frame->frame_obj); | ^~~~~~~~~~~~~~~~~~~~~~~~~~ --- Include/internal/pycore_interpframe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_interpframe.h b/Include/internal/pycore_interpframe.h index 812e1a28debef4..a85fa9bd32853c 100644 --- a/Include/internal/pycore_interpframe.h +++ b/Include/internal/pycore_interpframe.h @@ -345,7 +345,7 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame) { assert(!_PyFrame_IsIncomplete(frame)); - PyFrameObject *res = FT_ATOMIC_LOAD_PTR_ACQUIRE(frame->frame_obj); + PyFrameObject *res = (PyFrameObject*)FT_ATOMIC_LOAD_PTR_ACQUIRE(frame->frame_obj); if (res != NULL) { return res; } From 1414d2ac6f8a57ce5e0c8d3ab1fdc7075fd2d1ea Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 3 Sep 2026 18:48:32 +0300 Subject: [PATCH 3/4] gh-89735: Document requirements for the *headers* argument (GH-155640) Both handlers look up the header name in lowercase, and the basic one needs get_all(), so a plain dict does not work. Co-authored-by: Claude Opus 5 (1M context) --- Doc/library/urllib.request.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst index 9274a0c88ac4c8..7a0ccfd024beb3 100644 --- a/Doc/library/urllib.request.rst +++ b/Doc/library/urllib.request.rst @@ -1056,6 +1056,10 @@ AbstractBasicAuthHandler Objects authenticate for, *req* should be the (failed) :class:`Request` object, and *headers* should be the error headers. + *headers* must be a mapping-like object with case-insensitive lookup + that implements the ``get_all()`` method, + such as :class:`email.message.Message` or :class:`wsgiref.headers.Headers`. + *host* is either an authority (e.g. ``"python.org"``) or a URL containing an authority component (e.g. ``"https://python.org/"``). In either case, the authority must not contain a userinfo component (so, ``"python.org"`` and @@ -1097,6 +1101,9 @@ AbstractDigestAuthHandler Objects should be the (failed) :class:`Request` object, and *headers* should be the error headers. + *headers* must be a mapping-like object with case-insensitive lookup, + such as :class:`email.message.Message` or :class:`wsgiref.headers.Headers`. + .. _http-digest-auth-handler: From c8ea8676d931a3b42a428123070628f9ba8390db Mon Sep 17 00:00:00 2001 From: An Long Date: Fri, 4 Sep 2026 02:10:24 +0900 Subject: [PATCH 4/4] gh-94242: Clarify comments for _MAX_WINDOWS_WORKERS in concurrent.futures.process (#135537) --- Lib/concurrent/futures/process.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index c130259acb737a..7f4f225c0ad4fb 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -119,10 +119,11 @@ def _python_exit(): # On Windows, WaitForMultipleObjects is used to wait for processes to finish. -# It can wait on, at most, 63 objects. There is an overhead of two objects: +# It can wait on, at most, 64 objects. There is an overhead of three objects: # - the result queue reader # - the thread wakeup reader -_MAX_WINDOWS_WORKERS = 63 - 2 +# - the SIGINT handler +_MAX_WINDOWS_WORKERS = 64 - 3 # Hack to embed stringification of remote traceback in local traceback