From 79823c103b66030f10e07e04a5462f101674a4fc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 31 Aug 2023 13:53:19 +0200 Subject: [PATCH] gh-106320: Remove private _PyErr_ChainExceptions() (#108713) Remove _PyErr_ChainExceptions(), _PyErr_ChainExceptions1() and _PyErr_SetFromPyStatus() functions from the public C API. * Move the private _PyErr_ChainExceptions() and _PyErr_ChainExceptions1() function to the internal C API (pycore_pyerrors.h). * Move the private _PyErr_SetFromPyStatus() to the internal C API (pycore_initconfig.h). * No longer export the _PyErr_ChainExceptions() function. * Move run_in_subinterp_with_config() from _testcapi to _testinternalcapi. --- Include/cpython/initconfig.h | 1 - Include/cpython/pyerrors.h | 5 -- Include/internal/pycore_initconfig.h | 4 ++ Include/internal/pycore_pyerrors.h | 7 ++ Lib/test/support/__init__.py | 4 +- Lib/test/test_import/__init__.py | 8 +-- Modules/_io/_iomodule.c | 1 + Modules/_io/fileio.c | 1 + Modules/_io/iobase.c | 2 + Modules/_io/textio.c | 3 +- Modules/_sqlite/connection.c | 2 + Modules/_ssl.c | 1 + Modules/_testcapimodule.c | 101 -------------------------- Modules/_testinternalcapi.c | 103 +++++++++++++++++++++++++++ Modules/_xxsubinterpretersmodule.c | 6 ++ Modules/_zoneinfo.c | 2 +- Objects/odictobject.c | 5 +- Objects/weakrefobject.c | 1 + 18 files changed, 140 insertions(+), 117 deletions(-) diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index cbae97f12f5377..7fb7a9868be926 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -25,7 +25,6 @@ PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode); PyAPI_FUNC(int) PyStatus_IsError(PyStatus err); PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err); PyAPI_FUNC(int) PyStatus_Exception(PyStatus err); -PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status); /* --- PyWideStringList ------------------------------------------------ */ diff --git a/Include/cpython/pyerrors.h b/Include/cpython/pyerrors.h index cbe3be158b38be..9633a5407f28a6 100644 --- a/Include/cpython/pyerrors.h +++ b/Include/cpython/pyerrors.h @@ -88,11 +88,6 @@ typedef PyOSErrorObject PyEnvironmentErrorObject; typedef PyOSErrorObject PyWindowsErrorObject; #endif -/* Context manipulation (PEP 3134) */ - -Py_DEPRECATED(3.12) PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); -PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); - /* In exceptions.c */ PyAPI_FUNC(PyObject*) PyUnstable_Exc_PrepReraiseStar( diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h index c9645c7dc66573..6439101ff390ae 100644 --- a/Include/internal/pycore_initconfig.h +++ b/Include/internal/pycore_initconfig.h @@ -44,6 +44,10 @@ struct pyruntimestate; #define _PyStatus_UPDATE_FUNC(err) \ do { (err).func = _PyStatus_GET_FUNC(); } while (0) +// Export for '_testinternalcapi' shared extension +PyAPI_FUNC(PyObject *) _PyErr_SetFromPyStatus(PyStatus status); + + /* --- PyWideStringList ------------------------------------------------ */ #define _PyWideStringList_INIT (PyWideStringList){.length = 0, .items = NULL} diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index 0bc20589721f35..0f16fb894d17e1 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -163,6 +163,13 @@ PyAPI_FUNC(Py_ssize_t) _Py_UTF8_Edit_Cost(PyObject *str_a, PyObject *str_b, void _PyErr_FormatNote(const char *format, ...); +/* Context manipulation (PEP 3134) */ + +Py_DEPRECATED(3.12) extern void _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *); + +// Export for '_zoneinfo' shared extension +PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *); + #ifdef __cplusplus } #endif diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 328bddbdc6887b..c3f8527bd69525 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -1821,11 +1821,11 @@ def run_in_subinterp_with_config(code, *, own_gil=None, **config): module is enabled. """ _check_tracemalloc() - import _testcapi + import _testinternalcapi if own_gil is not None: assert 'gil' not in config, (own_gil, config) config['gil'] = 2 if own_gil else 1 - return _testcapi.run_in_subinterp_with_config(code, **config) + return _testinternalcapi.run_in_subinterp_with_config(code, **config) def _check_tracemalloc(): diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 051711bfd1fe24..740ce7d5ef2638 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1805,12 +1805,12 @@ def check_compatible_fresh(self, name, *, strict=False, isolated=False): check_multi_interp_extensions=strict, ) _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' - import _testcapi, sys + import _testinternalcapi, sys assert ( {name!r} in sys.builtin_module_names or {name!r} not in sys.modules ), repr({name!r}) - ret = _testcapi.run_in_subinterp_with_config( + ret = _testinternalcapi.run_in_subinterp_with_config( {self.import_script(name, "sys.stdout.fileno()")!r}, **{kwargs}, ) @@ -1829,9 +1829,9 @@ def check_incompatible_fresh(self, name, *, isolated=False): check_multi_interp_extensions=True, ) _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' - import _testcapi, sys + import _testinternalcapi, sys assert {name!r} not in sys.modules, {name!r} - ret = _testcapi.run_in_subinterp_with_config( + ret = _testinternalcapi.run_in_subinterp_with_config( {self.import_script(name, "sys.stdout.fileno()")!r}, **{kwargs}, ) diff --git a/Modules/_io/_iomodule.c b/Modules/_io/_iomodule.c index 0762e26a36c795..cc844527da5838 100644 --- a/Modules/_io/_iomodule.c +++ b/Modules/_io/_iomodule.c @@ -10,6 +10,7 @@ #include "Python.h" #include "pycore_abstract.h" // _PyNumber_Index() #include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "_iomodule.h" diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 15df1b2befda82..fb416700e22523 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH #include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include #ifdef HAVE_SYS_TYPES_H diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 55508a2214f84d..34fcd702391f32 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -12,6 +12,8 @@ #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyType_HasFeature() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() + #include // offsetof() #include "_iomodule.h" diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 8e44d453269a71..0a727a6e0ecd8a 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -9,10 +9,11 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallMethod() #include "pycore_codecs.h" // _PyCodecInfo_GetIncrementalDecoder() +#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() #include "pycore_interp.h" // PyInterpreterState.fs_codec #include "pycore_long.h" // _PyLong_GetZero() -#include "pycore_fileutils.h" // _Py_GetLocaleEncoding() #include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "_iomodule.h" diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 24090b0b63936a..21bdbc12814698 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -33,7 +33,9 @@ #include "blob.h" #include "prepare_protocol.h" #include "util.h" + #include "pycore_import.h" // _PyImport_GetModuleAttrString() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_weakref.h" // _PyWeakref_IS_DEAD() diff --git a/Modules/_ssl.c b/Modules/_ssl.c index be033256adc259..cecc3785c7661c 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -27,6 +27,7 @@ #include "Python.h" #include "pycore_fileutils.h" // _PyIsSelectable_fd() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_weakref.h" // _PyWeakref_GET_REF() /* Include symbols from _socket module */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 20b96320f4c339..d5c8a9d7ae520d 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1434,104 +1434,6 @@ run_in_subinterp(PyObject *self, PyObject *args) return PyLong_FromLong(r); } -/* To run some code in a sub-interpreter. */ -static PyObject * -run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) -{ - const char *code; - int use_main_obmalloc = -1; - int allow_fork = -1; - int allow_exec = -1; - int allow_threads = -1; - int allow_daemon_threads = -1; - int check_multi_interp_extensions = -1; - int gil = -1; - int r; - PyThreadState *substate, *mainstate; - /* only initialise 'cflags.cf_flags' to test backwards compatibility */ - PyCompilerFlags cflags = {0}; - - static char *kwlist[] = {"code", - "use_main_obmalloc", - "allow_fork", - "allow_exec", - "allow_threads", - "allow_daemon_threads", - "check_multi_interp_extensions", - "gil", - NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "s$ppppppi:run_in_subinterp_with_config", kwlist, - &code, &use_main_obmalloc, - &allow_fork, &allow_exec, - &allow_threads, &allow_daemon_threads, - &check_multi_interp_extensions, - &gil)) { - return NULL; - } - if (use_main_obmalloc < 0) { - PyErr_SetString(PyExc_ValueError, "missing use_main_obmalloc"); - return NULL; - } - if (allow_fork < 0) { - PyErr_SetString(PyExc_ValueError, "missing allow_fork"); - return NULL; - } - if (allow_exec < 0) { - PyErr_SetString(PyExc_ValueError, "missing allow_exec"); - return NULL; - } - if (allow_threads < 0) { - PyErr_SetString(PyExc_ValueError, "missing allow_threads"); - return NULL; - } - if (gil < 0) { - PyErr_SetString(PyExc_ValueError, "missing gil"); - return NULL; - } - if (allow_daemon_threads < 0) { - PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads"); - return NULL; - } - if (check_multi_interp_extensions < 0) { - PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions"); - return NULL; - } - - mainstate = PyThreadState_Get(); - - PyThreadState_Swap(NULL); - - const PyInterpreterConfig config = { - .use_main_obmalloc = use_main_obmalloc, - .allow_fork = allow_fork, - .allow_exec = allow_exec, - .allow_threads = allow_threads, - .allow_daemon_threads = allow_daemon_threads, - .check_multi_interp_extensions = check_multi_interp_extensions, - .gil = gil, - }; - PyStatus status = Py_NewInterpreterFromConfig(&substate, &config); - if (PyStatus_Exception(status)) { - /* Since no new thread state was created, there is no exception to - propagate; raise a fresh one after swapping in the old thread - state. */ - PyThreadState_Swap(mainstate); - _PyErr_SetFromPyStatus(status); - PyObject *exc = PyErr_GetRaisedException(); - PyErr_SetString(PyExc_RuntimeError, "sub-interpreter creation failed"); - _PyErr_ChainExceptions1(exc); - return NULL; - } - assert(substate != NULL); - r = PyRun_SimpleStringFlags(code, &cflags); - Py_EndInterpreter(substate); - - PyThreadState_Swap(mainstate); - - return PyLong_FromLong(r); -} - static void _xid_capsule_destructor(PyObject *capsule) { @@ -3376,9 +3278,6 @@ static PyMethodDef TestMethods[] = { {"crash_no_current_thread", crash_no_current_thread, METH_NOARGS}, {"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, - {"run_in_subinterp_with_config", - _PyCFunction_CAST(run_in_subinterp_with_config), - METH_VARARGS | METH_KEYWORDS}, {"get_crossinterp_data", get_crossinterp_data, METH_VARARGS}, {"restore_crossinterp_data", restore_crossinterp_data, METH_VARARGS}, {"create_cfunction", create_cfunction, METH_NOARGS}, diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 3e3dfeca037c7f..e375ca8556d217 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -25,6 +25,7 @@ #include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy() #include "pycore_object.h" // _PyObject_IsFreed() #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pyerrors.h" // _Py_UTF8_Edit_Cost() #include "pycore_pystate.h" // _PyThreadState_GET() @@ -1593,6 +1594,105 @@ dict_getitem_knownhash(PyObject *self, PyObject *args) } +/* To run some code in a sub-interpreter. */ +static PyObject * +run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *code; + int use_main_obmalloc = -1; + int allow_fork = -1; + int allow_exec = -1; + int allow_threads = -1; + int allow_daemon_threads = -1; + int check_multi_interp_extensions = -1; + int gil = -1; + int r; + PyThreadState *substate, *mainstate; + /* only initialise 'cflags.cf_flags' to test backwards compatibility */ + PyCompilerFlags cflags = {0}; + + static char *kwlist[] = {"code", + "use_main_obmalloc", + "allow_fork", + "allow_exec", + "allow_threads", + "allow_daemon_threads", + "check_multi_interp_extensions", + "gil", + NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "s$ppppppi:run_in_subinterp_with_config", kwlist, + &code, &use_main_obmalloc, + &allow_fork, &allow_exec, + &allow_threads, &allow_daemon_threads, + &check_multi_interp_extensions, + &gil)) { + return NULL; + } + if (use_main_obmalloc < 0) { + PyErr_SetString(PyExc_ValueError, "missing use_main_obmalloc"); + return NULL; + } + if (allow_fork < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_fork"); + return NULL; + } + if (allow_exec < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_exec"); + return NULL; + } + if (allow_threads < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_threads"); + return NULL; + } + if (gil < 0) { + PyErr_SetString(PyExc_ValueError, "missing gil"); + return NULL; + } + if (allow_daemon_threads < 0) { + PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads"); + return NULL; + } + if (check_multi_interp_extensions < 0) { + PyErr_SetString(PyExc_ValueError, "missing check_multi_interp_extensions"); + return NULL; + } + + mainstate = PyThreadState_Get(); + + PyThreadState_Swap(NULL); + + const PyInterpreterConfig config = { + .use_main_obmalloc = use_main_obmalloc, + .allow_fork = allow_fork, + .allow_exec = allow_exec, + .allow_threads = allow_threads, + .allow_daemon_threads = allow_daemon_threads, + .check_multi_interp_extensions = check_multi_interp_extensions, + .gil = gil, + }; + PyStatus status = Py_NewInterpreterFromConfig(&substate, &config); + if (PyStatus_Exception(status)) { + /* Since no new thread state was created, there is no exception to + propagate; raise a fresh one after swapping in the old thread + state. */ + PyThreadState_Swap(mainstate); + _PyErr_SetFromPyStatus(status); + PyObject *exc = PyErr_GetRaisedException(); + PyErr_SetString(PyExc_RuntimeError, "sub-interpreter creation failed"); + _PyErr_ChainExceptions1(exc); + return NULL; + } + assert(substate != NULL); + r = PyRun_SimpleStringFlags(code, &cflags); + Py_EndInterpreter(substate); + + PyThreadState_Swap(mainstate); + + return PyLong_FromLong(r); +} + + static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -1659,6 +1759,9 @@ static PyMethodDef module_functions[] = { {"get_object_dict_values", get_object_dict_values, METH_O}, {"hamt", new_hamt, METH_NOARGS}, {"dict_getitem_knownhash", dict_getitem_knownhash, METH_VARARGS}, + {"run_in_subinterp_with_config", + _PyCFunction_CAST(run_in_subinterp_with_config), + METH_VARARGS | METH_KEYWORDS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index ea91e70cad991d..6638c2c8e0636b 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1,7 +1,13 @@ /* interpreters module */ /* low-level access to interpreter primitives */ +#ifndef Py_BUILD_CORE_BUILTIN +# define Py_BUILD_CORE_MODULE 1 +#endif + #include "Python.h" +#include "pycore_initconfig.h" // _PyErr_SetFromPyStatus() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "interpreteridobject.h" diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index fb0b4b40b2ad5d..09f5fd4b2ef0a9 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -4,7 +4,7 @@ #include "Python.h" #include "pycore_long.h" // _PyLong_GetOne() - +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include #include diff --git a/Objects/odictobject.c b/Objects/odictobject.c index d7a0f914d41aff..b99896319e0136 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -465,10 +465,11 @@ Potential Optimizations */ #include "Python.h" -#include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_dict.h" // _Py_dict_lookup() +#include "pycore_object.h" // _PyObject_GC_UNTRACK() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include // offsetof() #include "clinic/odictobject.c.h" diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 1814c6eb69c29b..df74be6aba7244 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1,6 +1,7 @@ #include "Python.h" #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR() +#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_weakref.h" // _PyWeakref_GET_REF()