GeezerVison is showing github.com — gh-131591: Implement PEP 768 (#131937) · python/cpython@943cc14
File treeExpand file treeCollapse file tree Open diff view settings Misc/NEWS.d/next/Core_and_Builtins
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Doc/library/sys.rst +22Lines changed: 22 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1835,6 +1835,28 @@ always available. Unless explicitly noted otherwise, all variables are read-only | 1835 | 1835 |
| 1836 | 1836 | .. versionadded:: 3.12 | 1837 | 1837 |
| | 1838 | + | | 1839 | +.. function:: remote_exec(pid, script) | | 1840 | + | | 1841 | + Executes *script*, a file containing Python code in the remote | | 1842 | + process with the given *pid*. | | 1843 | + | | 1844 | + This function returns immediately, and the code will be executed by the | | 1845 | + target process's main thread at the next available opportunity, similarly | | 1846 | + to how signals are handled. There is no interface to determine when the | | 1847 | + code has been executed. The caller is responsible for making sure that | | 1848 | + the file still exists whenever the remote process tries to read it and that | | 1849 | + it hasn't been overwritten. | | 1850 | + | | 1851 | + The remote process must be running a CPython interpreter of the same major | | 1852 | + and minor version as the local process. If either the local or remote | | 1853 | + interpreter is pre-release (alpha, beta, or release candidate) then the | | 1854 | + local and remote interpreters must be the same exact version. | | 1855 | + | | 1856 | + .. availability:: Unix, Windows. | | 1857 | + .. versionadded:: next | | 1858 | + | | 1859 | + | 1838 | 1860 | .. function:: _enablelegacywindowsfsencoding() | 1839 | 1861 |
| 1840 | 1862 | Changes the :term:`filesystem encoding and error handler` to 'mbcs' and | |
Collapse file Copy file name to clipboardExpand all lines: Doc/using/cmdline.rst +20Lines changed: 20 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -603,6 +603,17 @@ Miscellaneous options | 603 | 603 |
| 604 | 604 | .. versionadded:: 3.13 | 605 | 605 |
| | 606 | + * ``-X disable_remote_debug`` disables the remote debugging support as described | | 607 | + in :pep:`768`. This includes both the functionality to schedule code for | | 608 | + execution in another process and the functionality to receive code for | | 609 | + execution in the current process. | | 610 | + | | 611 | + This option is only available on some platforms and will do nothing | | 612 | + if is not supported on the current system. See also | | 613 | + :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` and :pep:`768`. | | 614 | + | | 615 | + .. versionadded:: next | | 616 | + | 606 | 617 | * :samp:`-X cpu_count={n}` overrides :func:`os.cpu_count`, | 607 | 618 | :func:`os.process_cpu_count`, and :func:`multiprocessing.cpu_count`. | 608 | 619 | *n* must be greater than or equal to 1. | @@ -1160,7 +1171,16 @@ conflict. | 1160 | 1171 |
| 1161 | 1172 | .. versionadded:: 3.13 | 1162 | 1173 |
| | 1174 | +.. envvar:: PYTHON_DISABLE_REMOTE_DEBUG | | 1175 | + | | 1176 | + If this variable is set to a non-empty string, it disables the remote | | 1177 | + debugging feature described in :pep:`768`. This includes both the functionality | | 1178 | + to schedule code for execution in another process and the functionality to | | 1179 | + receive code for execution in the current process. | | 1180 | + | | 1181 | + See also the :option:`-X disable_remote_debug` command-line option. | 1163 | 1182 |
| | 1183 | + .. versionadded:: next | 1164 | 1184 |
| 1165 | 1185 | .. envvar:: PYTHON_CPU_COUNT | 1166 | 1186 |
| |
Collapse file Copy file name to clipboardExpand all lines: Doc/using/configure.rst +11Lines changed: 11 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -660,6 +660,17 @@ also be used to improve performance. | 660 | 660 | Add ``-fstrict-overflow`` to the C compiler flags (by default we add | 661 | 661 | ``-fno-strict-overflow`` instead). | 662 | 662 |
| | 663 | +.. option:: --without-remote-debug | | 664 | + | | 665 | + Deactivate remote debugging support described in :pep:`768` (enabled by default). | | 666 | + When this flag is provided the code that allows the interpreter to schedule the | | 667 | + execution of a Python file in a separate process as described in :pep:`768` is | | 668 | + not compiled. This includes both the functionality to schedule code to be executed | | 669 | + and the functionality to receive code to be executed. | | 670 | + | | 671 | + | | 672 | + .. versionadded:: next | | 673 | + | 663 | 674 |
| 664 | 675 | .. _debug-build: | 665 | 676 |
| |
Collapse file Copy file name to clipboardExpand all lines: Doc/whatsnew/3.14.rst +57Lines changed: 57 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -90,6 +90,63 @@ If you encounter :exc:`NameError`\s or pickling errors coming out of | 90 | 90 | New features | 91 | 91 | ============ | 92 | 92 |
| | 93 | +.. _whatsnew314-pep678: | | 94 | + | | 95 | +PEP 768: Safe external debugger interface for CPython | | 96 | +----------------------------------------------------- | | 97 | + | | 98 | +:pep:`768` introduces a zero-overhead debugging interface that allows debuggers and profilers | | 99 | +to safely attach to running Python processes. This is a significant enhancement to Python's | | 100 | +debugging capabilities allowing debuggers to forego unsafe alternatives. | | 101 | + | | 102 | +The new interface provides safe execution points for attaching debugger code without modifying | | 103 | +the interpreter's normal execution path or adding runtime overhead. This enables tools to | | 104 | +inspect and interact with Python applications in real-time without stopping or restarting | | 105 | +them — a crucial capability for high-availability systems and production environments. | | 106 | + | | 107 | +For convenience, CPython implements this interface through the :mod:`sys` module with a | | 108 | +:func:`sys.remote_exec` function:: | | 109 | + | | 110 | + sys.remote_exec(pid, script_path) | | 111 | + | | 112 | +This function allows sending Python code to be executed in a target process at the next safe | | 113 | +execution point. However, tool authors can also implement the protocol directly as described | | 114 | +in the PEP, which details the underlying mechanisms used to safely attach to running processes. | | 115 | + | | 116 | +Here's a simple example that inspects object types in a running Python process: | | 117 | + | | 118 | + .. code-block:: python | | 119 | +
| | 120 | + import os | | 121 | + import sys | | 122 | + import tempfile | | 123 | +
| | 124 | + # Create a temporary script | | 125 | + with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: | | 126 | + script_path = f.name | | 127 | + f.write(f"import my_debugger; my_debugger.connect({os.getpid()})") | | 128 | + try: | | 129 | + # Execute in process with PID 1234 | | 130 | + print("Behold! An offering:") | | 131 | + sys.remote_exec(1234, script_path) | | 132 | + finally: | | 133 | + os.unlink(script_path) | | 134 | +
| | 135 | +The debugging interface has been carefully designed with security in mind and includes several | | 136 | +mechanisms to control access: | | 137 | + | | 138 | +* A :envvar:`PYTHON_DISABLE_REMOTE_DEBUG` environment variable. | | 139 | +* A :option:`-X disable-remote-debug` command-line option. | | 140 | +* A :option:`--without-remote-debug` configure flag to completely disable the feature at build time. | | 141 | + | | 142 | +A key implementation detail is that the interface piggybacks on the interpreter's existing evaluation | | 143 | +loop and safe points, ensuring zero overhead during normal execution while providing a reliable way | | 144 | +for external processes to coordinate debugging operations. | | 145 | + | | 146 | +See :pep:`768` for more details. | | 147 | + | | 148 | +(Contributed by Pablo Galindo Salgado, Matt Wozniski, and Ivona Stojanovic in :gh:`131591`.) | | 149 | + | 93 | 150 | .. _whatsnew314-pep758: | 94 | 151 |
| 95 | 152 | PEP 758 – Allow except and except* expressions without parentheses | |
Collapse file Copy file name to clipboardExpand all lines: Include/cpython/initconfig.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -143,6 +143,7 @@ typedef struct PyConfig { | 143 | 143 | int faulthandler; | 144 | 144 | int tracemalloc; | 145 | 145 | int perf_profiling; | | 146 | + int remote_debug; | 146 | 147 | int import_time; | 147 | 148 | int code_debug_ranges; | 148 | 149 | int show_ref_count; | |
Collapse file Copy file name to clipboardExpand all lines: Include/cpython/pystate.h +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -29,6 +29,13 @@ typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *, int, PyObject *); | 29 | 29 | #define PyTrace_C_RETURN 6 | 30 | 30 | #define PyTrace_OPCODE 7 | 31 | 31 |
| | 32 | +/* Remote debugger support */ | | 33 | +#define MAX_SCRIPT_PATH_SIZE 512 | | 34 | +typedef struct _remote_debugger_support { | | 35 | + int32_t debugger_pending_call; | | 36 | + char debugger_script_path[MAX_SCRIPT_PATH_SIZE]; | | 37 | +} _PyRemoteDebuggerSupport; | | 38 | + | 32 | 39 | typedef struct _err_stackitem { | 33 | 40 | /* This struct represents a single execution context where we might | 34 | 41 | * be currently handling an exception. It is a per-coroutine state | @@ -202,6 +209,7 @@ struct _ts { | 202 | 209 | The PyThreadObject must hold the only reference to this value. | 203 | 210 | */ | 204 | 211 | PyObject *threading_local_sentinel; | | 212 | + _PyRemoteDebuggerSupport remote_debugger_support; | 205 | 213 | }; | 206 | 214 |
| 207 | 215 | # define Py_C_RECURSION_LIMIT 5000 | |
Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_ceval.h +12Lines changed: 12 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -347,6 +347,18 @@ void _Py_unset_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit); | 347 | 347 |
| 348 | 348 | PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyStackRef right, double value); | 349 | 349 |
| | 350 | +#ifndef Py_SUPPORTS_REMOTE_DEBUG | | 351 | + #if defined(__APPLE__) | | 352 | + # if !defined(TARGET_OS_OSX) | | 353 | +// Older macOS SDKs do not define TARGET_OS_OSX | | 354 | + # define TARGET_OS_OSX 1 | | 355 | + # endif | | 356 | + #endif | | 357 | + #if ((defined(__APPLE__) && TARGET_OS_OSX) || defined(MS_WINDOWS) || (defined(__linux__) && HAVE_PROCESS_VM_READV)) | | 358 | + # define Py_SUPPORTS_REMOTE_DEBUG 1 | | 359 | + #endif | | 360 | +#endif | | 361 | + | 350 | 362 | #ifdef __cplusplus | 351 | 363 | } | 352 | 364 | #endif | |
Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_debug_offsets.h +19Lines changed: 19 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -73,6 +73,7 @@ typedef struct _Py_DebugOffsets { | 73 | 73 | uint64_t id; | 74 | 74 | uint64_t next; | 75 | 75 | uint64_t threads_head; | | 76 | + uint64_t threads_main; | 76 | 77 | uint64_t gc; | 77 | 78 | uint64_t imports_modules; | 78 | 79 | uint64_t sysdict; | @@ -206,6 +207,15 @@ typedef struct _Py_DebugOffsets { | 206 | 207 | uint64_t gi_iframe; | 207 | 208 | uint64_t gi_frame_state; | 208 | 209 | } gen_object; | | 210 | + | | 211 | + struct _debugger_support { | | 212 | + uint64_t eval_breaker; | | 213 | + uint64_t remote_debugger_support; | | 214 | + uint64_t remote_debugging_enabled; | | 215 | + uint64_t debugger_pending_call; | | 216 | + uint64_t debugger_script_path; | | 217 | + uint64_t debugger_script_path_size; | | 218 | + } debugger_support; | 209 | 219 | } _Py_DebugOffsets; | 210 | 220 |
| 211 | 221 |
| @@ -223,6 +233,7 @@ typedef struct _Py_DebugOffsets { | 223 | 233 | .id = offsetof(PyInterpreterState, id), \ | 224 | 234 | .next = offsetof(PyInterpreterState, next), \ | 225 | 235 | .threads_head = offsetof(PyInterpreterState, threads.head), \ | | 236 | + .threads_main = offsetof(PyInterpreterState, threads.main), \ | 226 | 237 | .gc = offsetof(PyInterpreterState, gc), \ | 227 | 238 | .imports_modules = offsetof(PyInterpreterState, imports.modules), \ | 228 | 239 | .sysdict = offsetof(PyInterpreterState, sysdict), \ | @@ -326,6 +337,14 @@ typedef struct _Py_DebugOffsets { | 326 | 337 | .gi_iframe = offsetof(PyGenObject, gi_iframe), \ | 327 | 338 | .gi_frame_state = offsetof(PyGenObject, gi_frame_state), \ | 328 | 339 | }, \ | | 340 | + .debugger_support = { \ | | 341 | + .eval_breaker = offsetof(PyThreadState, eval_breaker), \ | | 342 | + .remote_debugger_support = offsetof(PyThreadState, remote_debugger_support), \ | | 343 | + .remote_debugging_enabled = offsetof(PyInterpreterState, config.remote_debug), \ | | 344 | + .debugger_pending_call = offsetof(_PyRemoteDebuggerSupport, debugger_pending_call), \ | | 345 | + .debugger_script_path = offsetof(_PyRemoteDebuggerSupport, debugger_script_path), \ | | 346 | + .debugger_script_path_size = MAX_SCRIPT_PATH_SIZE, \ | | 347 | + }, \ | 329 | 348 | } | 330 | 349 |
| 331 | 350 |
| |
Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_global_objects_fini_generated.h +1Lines changed: 1 addition & 0 deletions Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_global_strings.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -686,6 +686,7 @@ struct _Py_global_strings { | 686 | 686 | STRUCT_FOR_ID(salt) | 687 | 687 | STRUCT_FOR_ID(sched_priority) | 688 | 688 | STRUCT_FOR_ID(scheduler) | | 689 | + STRUCT_FOR_ID(script) | 689 | 690 | STRUCT_FOR_ID(second) | 690 | 691 | STRUCT_FOR_ID(security_attributes) | 691 | 692 | STRUCT_FOR_ID(seek) | |
|