diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 6b68c0d085589a1..99a7ed8b1cb7b8e 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -699,7 +699,7 @@ but need extra remarks for use as slots: .. soft-deprecated:: 3.15 - When not targetting older Python versions, pefer :c:macro:`!Py_tp_bases`. + When not targeting older Python versions, prefer :c:macro:`!Py_tp_bases`. The following slots do not correspond to public fields in the underlying structures: diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 4809fdb42bf3d77..5bdfe7c182de3ac 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -1366,7 +1366,7 @@ Control flow ``try`` blocks which are followed by ``except*`` clauses. The attributes are the same as for :class:`Try` but the :class:`ExceptHandler` nodes in ``handlers`` - are interpreted as ``except*`` blocks rather then ``except``. + are interpreted as ``except*`` blocks rather than ``except``. .. doctest:: diff --git a/Doc/library/compression.zstd.rst b/Doc/library/compression.zstd.rst index 6d99e36e1e5bb65..6618ccf7d559e78 100644 --- a/Doc/library/compression.zstd.rst +++ b/Doc/library/compression.zstd.rst @@ -503,7 +503,7 @@ Advanced parameter control The :meth:`~.bounds` method can be used on any attribute to get the valid values for that parameter. - Parameters are optional; any omitted parameter will have it's value selected + Parameters are optional; any omitted parameter will have its value selected automatically. Example getting the lower and upper bound of :attr:`~.compression_level`:: @@ -732,7 +732,7 @@ Advanced parameter control An :class:`~enum.IntEnum` containing the advanced decompression parameter keys that can be used when decompressing data. Parameters are optional; any - omitted parameter will have it's value selected automatically. + omitted parameter will have its value selected automatically. The :meth:`~.bounds` method can be used on any attribute to get the valid values for that parameter. diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 9a41e406c8c10e6..355621db2e18a18 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -2135,7 +2135,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only returned by the :func:`open` function. Their parameters are chosen as follows: - * The encoding and error handling are is initialized from + * The encoding and error handling are initialized from :c:member:`PyConfig.stdio_encoding` and :c:member:`PyConfig.stdio_errors`. On Windows, UTF-8 is used for the console device. Non-character diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 2e97fa5820f8245..70480ffe4c55c2a 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -220,7 +220,7 @@ def test_kill_issue43884(self): # kills the process and all its children. creationflags = CREATE_NEW_PROCESS_GROUP proc = self.loop.run_until_complete( - asyncio.create_subprocess_shell(blocking_shell_command, stdout=asyncio.subprocess.PIPE, + asyncio.create_subprocess_shell(blocking_shell_command, creationflags=creationflags) ) self.loop.run_until_complete(asyncio.sleep(1)) diff --git a/Lib/test/test_external_inspection.py b/Lib/test/test_external_inspection.py index 6b1529aa173f01c..910fe96d5e7d81e 100644 --- a/Lib/test/test_external_inspection.py +++ b/Lib/test/test_external_inspection.py @@ -438,6 +438,56 @@ def _extract_coroutine_stacks_lineno_only(self, stack_trace): # ============================================================================ +@requires_remote_subprocess_debugging() +class TestSelfStackTrace(RemoteInspectionTestBase): + @skip_if_not_supported + @unittest.skipIf( + sys.platform == "linux" and not PROCESS_VM_READV_SUPPORTED, + "Test only runs on Linux with process_vm_readv support", + ) + def test_self_trace_with_large_linetable(self): + script = textwrap.dedent("""\ + import os + import _remote_debugging + + assignments = "\\n".join( + f"value_{i} = {i}" for i in range(1000) + ) + expected_lineno = len(assignments.splitlines()) + 1 + source = ( + f"{assignments}\\n" + "stack_trace = " + "_remote_debugging.RemoteUnwinder(os.getpid()).get_stack_trace()\\n" + ) + code = compile(source, "large_linetable.py", "exec") + assert len(code.co_linetable) > 4096, len(code.co_linetable) + namespace = {"os": os, "_remote_debugging": _remote_debugging} + exec(code, namespace) + large_linetable_frames = [ + frame + for interpreter in namespace["stack_trace"] + for thread in interpreter.threads + for frame in thread.frame_info + if frame.filename == "large_linetable.py" + ] + assert len(large_linetable_frames) == 1, large_linetable_frames + assert large_linetable_frames[0].location.lineno == expected_lineno, ( + large_linetable_frames[0] + ) + """) + + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + timeout=SHORT_TIMEOUT, + ) + self.assertEqual( + result.returncode, 0, + f"stdout: {result.stdout}\nstderr: {result.stderr}" + ) + + @requires_remote_subprocess_debugging() class TestGetStackTrace(RemoteInspectionTestBase): @skip_if_not_supported diff --git a/Lib/test/test_winapi.py b/Lib/test/test_winapi.py index a1c0b80d47e4d44..0ae03a3bf505f73 100644 --- a/Lib/test/test_winapi.py +++ b/Lib/test/test_winapi.py @@ -12,9 +12,16 @@ MAXIMUM_WAIT_OBJECTS = 64 MAXIMUM_BATCHED_WAIT_OBJECTS = (MAXIMUM_WAIT_OBJECTS - 1) ** 2 + +def close_events(events): + for handle in events: + _winapi.CloseHandle(handle) + + class WinAPIBatchedWaitForMultipleObjectsTests(unittest.TestCase): def _events_waitall_test(self, n): evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)] + self.addCleanup(close_events, evts) with self.assertRaises(TimeoutError): _winapi.BatchedWaitForMultipleObjects(evts, True, 100) @@ -42,6 +49,7 @@ def _events_waitall_test(self, n): def _events_waitany_test(self, n): evts = [_winapi.CreateEventW(0, False, False, None) for _ in range(n)] + self.addCleanup(close_events, evts) with self.assertRaises(TimeoutError): _winapi.BatchedWaitForMultipleObjects(evts, False, 100) diff --git a/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst b/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst new file mode 100644 index 000000000000000..f16f024c582fb3c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-06-15-04-37.gh-issue-151022.1Aw8Tk.rst @@ -0,0 +1,2 @@ +Fix ``_remote_debugging`` stack traces for code objects with large line +tables. diff --git a/Modules/_remote_debugging/code_objects.c b/Modules/_remote_debugging/code_objects.c index ab889a130ee4e7e..f83252524b96ff4 100644 --- a/Modules/_remote_debugging/code_objects.c +++ b/Modules/_remote_debugging/code_objects.c @@ -7,6 +7,8 @@ #include "_remote_debugging.h" +#define MAX_LINETABLE_SIZE (64 * 1024) + /* ============================================================================ * TLBC CACHING FUNCTIONS (Py_GIL_DISABLED only) * ============================================================================ */ @@ -186,11 +188,7 @@ parse_linetable(const uintptr_t addrq, const char* linetable, Py_ssize_t linetab int computed_line = firstlineno; // Running accumulator, separate from output int val; // Temporary for varint results uint8_t byte; // Temporary for byte reads - const size_t MAX_LINETABLE_ENTRIES = 65536; - size_t entry_count = 0; - - while (ptr < end && *ptr != '\0' && entry_count < MAX_LINETABLE_ENTRIES) { - entry_count++; + while (ptr < end && *ptr != '\0') { uint8_t first_byte = *(ptr++); uint8_t code = (first_byte >> 3) & 15; size_t length = (first_byte & 7) + 1; @@ -387,7 +385,8 @@ parse_code_object(RemoteUnwinderObject *unwinder, } linetable = read_py_bytes(unwinder, - GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), 4096); + GET_MEMBER(uintptr_t, code_object, unwinder->debug_offsets.code_object.linetable), + MAX_LINETABLE_SIZE); if (!linetable) { set_exception_cause(unwinder, PyExc_RuntimeError, "Failed to read linetable from code object"); goto error; diff --git a/Platforms/emscripten/config.toml b/Platforms/emscripten/config.toml index 0c65a6623c9798a..5e4f80b28a41c18 100644 --- a/Platforms/emscripten/config.toml +++ b/Platforms/emscripten/config.toml @@ -1,7 +1,7 @@ # Any data that can vary between Python versions is to be kept in this file. # This allows for blanket copying of the Emscripten build code between supported # Python versions. -emscripten-version = "6.0.2" +emscripten-version = "6.0.3" node-version = "24" test-args = [ "-m", "test", diff --git a/configure b/configure index 0fc2b0e819a1209..b7ba4994b30e701 100755 --- a/configure +++ b/configure @@ -9807,7 +9807,6 @@ fi as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET" as_fn_append LINKFORSHARED " -sSTACK_SIZE=5MB" as_fn_append LINKFORSHARED " -sTEXTDECODER=2" - as_fn_append LINKFORSHARED " -sGROWABLE_ARRAYBUFFERS=0" if test "x$enable_wasm_dynamic_linking" = xyes then : diff --git a/configure.ac b/configure.ac index 980ed2af50b13f7..5e98a4f91c8d5ed 100644 --- a/configure.ac +++ b/configure.ac @@ -2410,8 +2410,6 @@ AS_CASE([$ac_sys_system], AS_VAR_APPEND([LINKFORSHARED], [" -sSTACK_SIZE=5MB"]) dnl Avoid bugs in JS fallback string decoding path AS_VAR_APPEND([LINKFORSHARED], [" -sTEXTDECODER=2"]) - dnl workaround for emscripten#27241, can remove when we update to emscripten 6.0.3 - AS_VAR_APPEND([LINKFORSHARED], [" -sGROWABLE_ARRAYBUFFERS=0"]) AS_VAR_IF([enable_wasm_dynamic_linking], [yes], [ AS_VAR_APPEND([LINKFORSHARED], [" -sMAIN_MODULE"])