From d0c95ed262fa1b22ac9ddef6f0c26f8fffe7484d Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Thu, 5 Mar 2026 00:04:41 -0500 Subject: [PATCH 1/6] Export _Py_DumpTraceback and _Py_DumpTracebackThreads as PyUnstable_ functions These functions stopped being exported in #107215. However, they are the only way to print a Python stacktrace safely from a signal handler, making them very useful for extensions. Re-export them as PyUnstable functions. --- Include/internal/pycore_traceback.h | 49 ------------------- Include/traceback.h | 44 +++++++++++++++++ Modules/faulthandler.c | 15 +++--- Python/pylifecycle.c | 6 +-- Python/traceback.c | 8 +-- Tools/wasm/emscripten/node_entry.mjs | 2 +- .../web_example_pyrepl_jspi/src.mjs | 2 +- configure | 2 +- configure.ac | 2 +- 9 files changed, 63 insertions(+), 67 deletions(-) diff --git a/Include/internal/pycore_traceback.h b/Include/internal/pycore_traceback.h index 8357cce9d899fb..e7729965b71769 100644 --- a/Include/internal/pycore_traceback.h +++ b/Include/internal/pycore_traceback.h @@ -14,55 +14,6 @@ PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int, int *, P // Export for 'pyexact' shared extension PyAPI_FUNC(void) _PyTraceback_Add(const char *, const char *, int); -/* Write the Python traceback into the file 'fd'. For example: - - Traceback (most recent call first): - File "xxx", line xxx in - File "xxx", line xxx in - ... - File "xxx", line xxx in - - This function is written for debug purpose only, to dump the traceback in - the worst case: after a segmentation fault, at fatal error, etc. That's why, - it is very limited. Strings are truncated to 100 characters and encoded to - ASCII with backslashreplace. It doesn't write the source code, only the - function name, filename and line number of each frame. Write only the first - 100 frames: if the traceback is truncated, write the line " ...". - - This function is signal safe. */ - -extern void _Py_DumpTraceback( - int fd, - PyThreadState *tstate); - -/* Write the traceback of all threads into the file 'fd'. current_thread can be - NULL. - - Return NULL on success, or an error message on error. - - This function is written for debug purpose only. It calls - _Py_DumpTraceback() for each thread, and so has the same limitations. It - only write the traceback of the first 100 threads: write "..." if there are - more threads. - - If current_tstate is NULL, the function tries to get the Python thread state - of the current thread. It is not an error if the function is unable to get - the current Python thread state. - - If interp is NULL, the function tries to get the interpreter state from - the current Python thread state, or from - _PyGILState_GetInterpreterStateUnsafe() in last resort. - - It is better to pass NULL to interp and current_tstate, the function tries - different options to retrieve this information. - - This function is signal safe. */ - -extern const char* _Py_DumpTracebackThreads( - int fd, - PyInterpreterState *interp, - PyThreadState *current_tstate); - /* Write a Unicode object into the file descriptor fd. Encode the string to ASCII using the backslashreplace error handler. diff --git a/Include/traceback.h b/Include/traceback.h index 2b40cc9fc32617..f15a01d5a24bde 100644 --- a/Include/traceback.h +++ b/Include/traceback.h @@ -13,6 +13,50 @@ PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); PyAPI_DATA(PyTypeObject) PyTraceBack_Type; #define PyTraceBack_Check(v) Py_IS_TYPE((v), &PyTraceBack_Type) +/* Write the Python traceback into the file 'fd'. For example: + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + + This function is written for debug purpose only, to dump the traceback in + the worst case: after a segmentation fault, at fatal error, etc. That's why, + it is very limited. Strings are truncated to 100 characters and encoded to + ASCII with backslashreplace. It doesn't write the source code, only the + function name, filename and line number of each frame. Write only the first + 100 frames: if the traceback is truncated, write the line " ...". + + This function is signal safe. */ +PyAPI_FUNC(void) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate); + +/* Write the traceback of all threads into the file 'fd'. current_thread can be + NULL. + + Return NULL on success, or an error message on error. + + This function is written for debug purpose only. It calls + PyUnstable_DumpTraceback() for each thread, and so has the same limitations. It + only writes the traceback of the first 100 threads: write "..." if there are + more threads. + + If current_tstate is NULL, the function tries to get the Python thread state + of the current thread. It is not an error if the function is unable to get + the current Python thread state. + + If interp is NULL, the function tries to get the interpreter state from + the current Python thread state, or from + _PyGILState_GetInterpreterStateUnsafe() in last resort. + + It is better to pass NULL to interp and current_tstate, the function tries + different options to retrieve this information. + + This function is signal safe. */ +PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads( + int fd, + PyInterpreterState *interp, + PyThreadState *current_tstate); #ifndef Py_LIMITED_API # define Py_CPYTHON_TRACEBACK_H diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 9b8c77e2b0401f..28cd41606dac2c 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -7,7 +7,7 @@ #include "pycore_runtime.h" // _Py_ID() #include "pycore_signal.h" // Py_NSIG #include "pycore_time.h" // _PyTime_FromSecondsObject() -#include "pycore_traceback.h" // _Py_DumpTracebackThreads +#include "pycore_traceback.h" // _Py_DumpStack() #ifdef HAVE_UNISTD_H # include // _exit() #endif @@ -205,14 +205,15 @@ faulthandler_dump_traceback(int fd, int all_threads, PyThreadState *tstate = PyGILState_GetThisThreadState(); if (all_threads == 1) { - (void)_Py_DumpTracebackThreads(fd, NULL, tstate); + (void)PyUnstable_DumpTracebackThreads(fd, NULL, tstate); } else { if (all_threads == FT_IGNORE_ALL_THREADS) { PUTS(fd, "\n"); } - if (tstate != NULL) - _Py_DumpTraceback(fd, tstate); + if (tstate != NULL) { + PyUnstable_DumpTraceback(fd, tstate); + } } reentrant = 0; @@ -273,7 +274,7 @@ faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file, /* gh-128400: Accessing other thread states while they're running * isn't safe if those threads are running. */ _PyEval_StopTheWorld(interp); - errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate); + errmsg = PyUnstable_DumpTracebackThreads(fd, NULL, tstate); _PyEval_StartTheWorld(interp); if (errmsg != NULL) { PyErr_SetString(PyExc_RuntimeError, errmsg); @@ -282,7 +283,7 @@ faulthandler_dump_traceback_py_impl(PyObject *module, PyObject *file, } } else { - _Py_DumpTraceback(fd, tstate); + PyUnstable_DumpTraceback(fd, tstate); } Py_XDECREF(file); @@ -703,7 +704,7 @@ faulthandler_thread(void *unused) (void)_Py_write_noraise(thread.fd, thread.header, (int)thread.header_len); - errmsg = _Py_DumpTracebackThreads(thread.fd, thread.interp, NULL); + errmsg = PyUnstable_DumpTracebackThreads(thread.fd, thread.interp, NULL); ok = (errmsg == NULL); if (thread.exit) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 711e7bc89b71c0..7d328cd3808401 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -29,7 +29,7 @@ #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_stats.h" // _PyStats_InterpInit() #include "pycore_sysmodule.h" // _PySys_ClearAttrString() -#include "pycore_traceback.h" // _Py_DumpTracebackThreads() +#include "pycore_traceback.h" // _Py_DumpTraceback_Init() #include "pycore_typeobject.h" // _PyTypes_InitTypes() #include "pycore_typevarobject.h" // _Py_clear_generic_types() #include "pycore_unicodeobject.h" // _PyUnicode_InitTypes() @@ -3208,9 +3208,9 @@ _Py_FatalError_DumpTracebacks(int fd, PyInterpreterState *interp, /* display the current Python stack */ #ifndef Py_GIL_DISABLED - _Py_DumpTracebackThreads(fd, interp, tstate); + PyUnstable_DumpTracebackThreads(fd, interp, tstate); #else - _Py_DumpTraceback(fd, tstate); + PyUnstable_DumpTraceback(fd, tstate); #endif } diff --git a/Python/traceback.c b/Python/traceback.c index 74360a1c73c271..cc1796a3132f9c 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -1168,7 +1168,7 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ void -_Py_DumpTraceback(int fd, PyThreadState *tstate) +PyUnstable_DumpTraceback(int fd, PyThreadState *tstate) { dump_traceback(fd, tstate, 1); } @@ -1264,11 +1264,11 @@ write_thread_id(int fd, PyThreadState *tstate, int is_current) The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ const char* _Py_NO_SANITIZE_THREAD -_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, - PyThreadState *current_tstate) +PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, + PyThreadState *current_tstate) { if (current_tstate == NULL) { - /* _Py_DumpTracebackThreads() is called from signal handlers by + /* PyUnstable_DumpTracebackThreads() is called from signal handlers by faulthandler. SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals diff --git a/Tools/wasm/emscripten/node_entry.mjs b/Tools/wasm/emscripten/node_entry.mjs index 166df40742b7fc..424abd70e24b28 100644 --- a/Tools/wasm/emscripten/node_entry.mjs +++ b/Tools/wasm/emscripten/node_entry.mjs @@ -53,6 +53,6 @@ try { // Show JavaScript exception and traceback console.warn(e); // Show Python exception and traceback - Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); + Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); process.exit(1); } diff --git a/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs b/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs index 5642372c9d2472..38a622117c2a50 100644 --- a/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs +++ b/Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs @@ -189,6 +189,6 @@ try { // Show JavaScript exception and traceback console.warn(e); // Show Python exception and traceback - Module.__Py_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); + Module.PyUnstable_DumpTraceback(2, Module._PyGILState_GetThisThreadState()); process.exit(1); } diff --git a/configure b/configure index eca5f03cdcfb2d..a1e7fb2e868143 100755 --- a/configure +++ b/configure @@ -9651,7 +9651,7 @@ fi as_fn_append LINKFORSHARED " -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js" as_fn_append LINKFORSHARED " -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY" - as_fn_append LINKFORSHARED " -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__Py_DumpTraceback,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET" + 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" diff --git a/configure.ac b/configure.ac index c21024a1e77433..098d574350521d 100644 --- a/configure.ac +++ b/configure.ac @@ -2357,7 +2357,7 @@ AS_CASE([$ac_sys_system], dnl Include file system support AS_VAR_APPEND([LINKFORSHARED], [" -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"]) AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_RUNTIME_METHODS=FS,callMain,ENV,HEAPU32,TTY"]) - AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__Py_DumpTraceback,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"]) + AS_VAR_APPEND([LINKFORSHARED], [" -sEXPORTED_FUNCTIONS=_main,_Py_Version,__PyRuntime,_PyGILState_GetThisThreadState,__PyEM_EMSCRIPTEN_TRAMPOLINE_OFFSET"]) AS_VAR_APPEND([LINKFORSHARED], [" -sSTACK_SIZE=5MB"]) dnl Avoid bugs in JS fallback string decoding path AS_VAR_APPEND([LINKFORSHARED], [" -sTEXTDECODER=2"]) From 538a1cd30792a56b685354ec0ce35676926ee5b3 Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Thu, 5 Mar 2026 16:17:36 -0500 Subject: [PATCH 2/6] Add written docs, move decls to cpython/traceback.h to avoid modifying Limited API --- Doc/c-api/exceptions.rst | 17 ++++++++++++++ Include/cpython/traceback.h | 45 +++++++++++++++++++++++++++++++++++++ Include/traceback.h | 45 ------------------------------------- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index aef191d3a29ac6..c726b083ae21f7 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -1346,3 +1346,20 @@ Tracebacks This function returns ``0`` on success, and returns ``-1`` with an exception set on failure. + +.. c:function:: void PyUnstable_DumpTraceback(int fd, PyThreadState *tstate) + + Write a trace of the Python stack in *tstate* into the file *fd*. + + This function is safe to use from signal handlers. + +.. c:function:: const char* PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate) + + Write the traces of all Python threads in *interp* into the file *fd*. If + *current_state* is not NULL then it will be used to identify what the current + thread is in the written output. If it is NULL then this function will + identify the current thread using thread-specific storage. + + This function will return NULL on success, or an error message on error. + + This function is safe to use from signal handlers. diff --git a/Include/cpython/traceback.h b/Include/cpython/traceback.h index 81c51944f136f2..3f7cbd354fa0b9 100644 --- a/Include/cpython/traceback.h +++ b/Include/cpython/traceback.h @@ -11,3 +11,48 @@ struct _traceback { int tb_lasti; int tb_lineno; }; + +/* Write the Python traceback into the file 'fd'. For example: + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + + This function is written for debug purpose only, to dump the traceback in + the worst case: after a segmentation fault, at fatal error, etc. That's why, + it is very limited. Strings are truncated to 100 characters and encoded to + ASCII with backslashreplace. It doesn't write the source code, only the + function name, filename and line number of each frame. Write only the first + 100 frames: if the traceback is truncated, write the line " ...". + + This function is signal safe. */ +PyAPI_FUNC(void) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate); + +/* Write the traceback of all threads into the file 'fd'. current_thread can be + NULL. + + Return NULL on success, or an error message on error. + + This function is written for debug purpose only. It calls + PyUnstable_DumpTraceback() for each thread, and so has the same limitations. It + only writes the traceback of the first 100 threads: write "..." if there are + more threads. + + If current_tstate is NULL, the function tries to get the Python thread state + of the current thread. It is not an error if the function is unable to get + the current Python thread state. + + If interp is NULL, the function tries to get the interpreter state from + the current Python thread state, or from + _PyGILState_GetInterpreterStateUnsafe() in last resort. + + It is better to pass NULL to interp and current_tstate, the function tries + different options to retrieve this information. + + This function is signal safe. */ +PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads( + int fd, + PyInterpreterState *interp, + PyThreadState *current_tstate); diff --git a/Include/traceback.h b/Include/traceback.h index f15a01d5a24bde..53501c68b79663 100644 --- a/Include/traceback.h +++ b/Include/traceback.h @@ -13,51 +13,6 @@ PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); PyAPI_DATA(PyTypeObject) PyTraceBack_Type; #define PyTraceBack_Check(v) Py_IS_TYPE((v), &PyTraceBack_Type) -/* Write the Python traceback into the file 'fd'. For example: - - Traceback (most recent call first): - File "xxx", line xxx in - File "xxx", line xxx in - ... - File "xxx", line xxx in - - This function is written for debug purpose only, to dump the traceback in - the worst case: after a segmentation fault, at fatal error, etc. That's why, - it is very limited. Strings are truncated to 100 characters and encoded to - ASCII with backslashreplace. It doesn't write the source code, only the - function name, filename and line number of each frame. Write only the first - 100 frames: if the traceback is truncated, write the line " ...". - - This function is signal safe. */ -PyAPI_FUNC(void) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate); - -/* Write the traceback of all threads into the file 'fd'. current_thread can be - NULL. - - Return NULL on success, or an error message on error. - - This function is written for debug purpose only. It calls - PyUnstable_DumpTraceback() for each thread, and so has the same limitations. It - only writes the traceback of the first 100 threads: write "..." if there are - more threads. - - If current_tstate is NULL, the function tries to get the Python thread state - of the current thread. It is not an error if the function is unable to get - the current Python thread state. - - If interp is NULL, the function tries to get the interpreter state from - the current Python thread state, or from - _PyGILState_GetInterpreterStateUnsafe() in last resort. - - It is better to pass NULL to interp and current_tstate, the function tries - different options to retrieve this information. - - This function is signal safe. */ -PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads( - int fd, - PyInterpreterState *interp, - PyThreadState *current_tstate); - #ifndef Py_LIMITED_API # define Py_CPYTHON_TRACEBACK_H # include "cpython/traceback.h" From e497d2e5b8a13e87b279deb5f618191d1f623722 Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Fri, 6 Mar 2026 11:05:27 -0500 Subject: [PATCH 3/6] Move comments into Doc/c-api, fix documented string length Comment says 100, but it appears to have been 500 since 2012, https://github.com/alexmalyshev/cpython/commit/54f939b9ae9cf91ed4cb3cd1b0f1e6aaff19782a. --- Doc/c-api/exceptions.rst | 50 ++++++++++++++++++++++++++++++++----- Include/cpython/traceback.h | 38 ---------------------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index c726b083ae21f7..df3f2c8181b10a 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -1349,17 +1349,55 @@ Tracebacks .. c:function:: void PyUnstable_DumpTraceback(int fd, PyThreadState *tstate) - Write a trace of the Python stack in *tstate* into the file *fd*. + Write a trace of the Python stack in *tstate* into the file *fd*. The format + looks like:: + + Traceback (most recent call first): + File "xxx", line xxx in + File "xxx", line xxx in + ... + File "xxx", line xxx in + + This function is meant to debug situations such as segfaults, fatal errors, + .etc. The file and function names it outputs are encoded to ASCII with + backslashreplace and truncated to 500 characters. It writes only the first + 100 frames, further frames are truncated with the line " ...". This function is safe to use from signal handlers. + The caller does not need to hold an :term:`attached thread state`, nor does + *tstate* need to be attached. + + .. versionadded:: next + .. c:function:: const char* PyUnstable_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate) - Write the traces of all Python threads in *interp* into the file *fd*. If - *current_state* is not NULL then it will be used to identify what the current - thread is in the written output. If it is NULL then this function will - identify the current thread using thread-specific storage. + Write the traces of all Python threads in *interp* into the file *fd*. + + If *interp* is ``NULL`` then this function will try to identify the current + interpreter using thread-specific storage. If it cannot, it will return an + error. + + If *current_tstate* is not ``NULL`` then it will be used to identify what the + current thread is in the written output. If it is ``NULL`` then this function + will identify the current thread using thread-specific storage. It is not an + error if the function is unable to get the current Python thread state. - This function will return NULL on success, or an error message on error. + This function will return ``NULL`` on success, or an error message on error. + + This function is meant to debug debug situations such as segfaults, fatal + errors, .etc. It calls :c:func:`PyUnsafe_DumpTraceback` for each thread. It + only writes the tracebacks of the first 100 threads, further output is + truncated with the line " ...". This function is safe to use from signal handlers. + + The caller does not need to hold an :term:`attached thread state`, nor does + *current_tstate* need to be attached. + + .. warning:: + On the :term:`free-threaded build`, this function is not thread-safe. If + another thread deletes its :term:`thread state` while this function is being + called, the process will likely crash. + + .. versionadded:: next diff --git a/Include/cpython/traceback.h b/Include/cpython/traceback.h index 3f7cbd354fa0b9..1667db9518b4f7 100644 --- a/Include/cpython/traceback.h +++ b/Include/cpython/traceback.h @@ -12,46 +12,8 @@ struct _traceback { int tb_lineno; }; -/* Write the Python traceback into the file 'fd'. For example: - - Traceback (most recent call first): - File "xxx", line xxx in - File "xxx", line xxx in - ... - File "xxx", line xxx in - - This function is written for debug purpose only, to dump the traceback in - the worst case: after a segmentation fault, at fatal error, etc. That's why, - it is very limited. Strings are truncated to 100 characters and encoded to - ASCII with backslashreplace. It doesn't write the source code, only the - function name, filename and line number of each frame. Write only the first - 100 frames: if the traceback is truncated, write the line " ...". - - This function is signal safe. */ PyAPI_FUNC(void) PyUnstable_DumpTraceback(int fd, PyThreadState *tstate); -/* Write the traceback of all threads into the file 'fd'. current_thread can be - NULL. - - Return NULL on success, or an error message on error. - - This function is written for debug purpose only. It calls - PyUnstable_DumpTraceback() for each thread, and so has the same limitations. It - only writes the traceback of the first 100 threads: write "..." if there are - more threads. - - If current_tstate is NULL, the function tries to get the Python thread state - of the current thread. It is not an error if the function is unable to get - the current Python thread state. - - If interp is NULL, the function tries to get the interpreter state from - the current Python thread state, or from - _PyGILState_GetInterpreterStateUnsafe() in last resort. - - It is better to pass NULL to interp and current_tstate, the function tries - different options to retrieve this information. - - This function is signal safe. */ PyAPI_FUNC(const char*) PyUnstable_DumpTracebackThreads( int fd, PyInterpreterState *interp, From 50e65013751ed0d6b26a58ea97fa004146aad783 Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Fri, 6 Mar 2026 16:43:37 -0500 Subject: [PATCH 4/6] Address comments on C API docs, add whatsnew entry --- Doc/c-api/exceptions.rst | 10 +++++----- Doc/whatsnew/3.15.rst | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index df3f2c8181b10a..6142097f8304a1 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -1359,9 +1359,9 @@ Tracebacks File "xxx", line xxx in This function is meant to debug situations such as segfaults, fatal errors, - .etc. The file and function names it outputs are encoded to ASCII with + and similar. The file and function names it outputs are encoded to ASCII with backslashreplace and truncated to 500 characters. It writes only the first - 100 frames, further frames are truncated with the line " ...". + 100 frames; further frames are truncated with the line ``...``. This function is safe to use from signal handlers. @@ -1386,9 +1386,9 @@ Tracebacks This function will return ``NULL`` on success, or an error message on error. This function is meant to debug debug situations such as segfaults, fatal - errors, .etc. It calls :c:func:`PyUnsafe_DumpTraceback` for each thread. It - only writes the tracebacks of the first 100 threads, further output is - truncated with the line " ...". + errors, and similar. It calls :c:func:`PyUnstable_DumpTraceback` for each + thread. It only writes the tracebacks of the first 100 threads, further + output is truncated with the line ``...``. This function is safe to use from signal handlers. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index fff2168be72604..10f715268d726f 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1668,6 +1668,11 @@ New features Python 3.14. (Contributed by Victor Stinner in :gh:`142417`.) +* Add :c:func:`PyUnstable_DumpTraceback` and `PyUnstable_DumpTracebackThreads` + functions to safely output Python stacktraces. These were previously internal + functions ``_Py_DumpTraceback`` and ``_Py_DumpTracebackThreads``. + (Contributed by Alex Malyshev in :gh:`145559`.) + Changed C APIs -------------- From 71c6fdd9e2c1f0ba8488c7219506ddf38010d859 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:50:49 +0000 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/C_API/2026-03-06-21-50-48.gh-issue-145559.AiXgHq.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/C_API/2026-03-06-21-50-48.gh-issue-145559.AiXgHq.rst diff --git a/Misc/NEWS.d/next/C_API/2026-03-06-21-50-48.gh-issue-145559.AiXgHq.rst b/Misc/NEWS.d/next/C_API/2026-03-06-21-50-48.gh-issue-145559.AiXgHq.rst new file mode 100644 index 00000000000000..5fa9877ce49c7e --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-03-06-21-50-48.gh-issue-145559.AiXgHq.rst @@ -0,0 +1 @@ +Rename `_Py_DumpTraceback` and `_Py_DumpTracebackThreads` to `PyUnstable_DumpTraceback` and `PyUnstable_DumpTracebackThreads`. From eb76a51889515db4dce2304c95c426447b1ed94c Mon Sep 17 00:00:00 2001 From: Alex Malyshev Date: Fri, 6 Mar 2026 16:52:59 -0500 Subject: [PATCH 6/6] Fix missing :c:func: --- Doc/whatsnew/3.15.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 10f715268d726f..9b9e977f1626e7 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1668,9 +1668,10 @@ New features Python 3.14. (Contributed by Victor Stinner in :gh:`142417`.) -* Add :c:func:`PyUnstable_DumpTraceback` and `PyUnstable_DumpTracebackThreads` - functions to safely output Python stacktraces. These were previously internal - functions ``_Py_DumpTraceback`` and ``_Py_DumpTracebackThreads``. +* Add :c:func:`PyUnstable_DumpTraceback` and + :c:func:`PyUnstable_DumpTracebackThreads` functions to safely output Python + stacktraces. These were previously internal functions ``_Py_DumpTraceback`` + and ``_Py_DumpTracebackThreads``. (Contributed by Alex Malyshev in :gh:`145559`.) Changed C APIs