Skip to content

Commit

Permalink
gh-106320: Remove private _PyErr_ChainExceptions() (#108713)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vstinner committed Aug 31, 2023
1 parent 059bd4d commit 79823c1
Show file tree
Hide file tree
Showing 18 changed files with 140 additions and 117 deletions.
1 change: 0 additions & 1 deletion Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ------------------------------------------------ */

Expand Down
5 changes: 0 additions & 5 deletions Include/cpython/pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
7 changes: 7 additions & 0 deletions Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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},
)
Expand All @@ -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},
)
Expand Down
1 change: 1 addition & 0 deletions Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stdbool.h>
#ifdef HAVE_SYS_TYPES_H
Expand Down
2 changes: 2 additions & 0 deletions Modules/_io/iobase.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <stddef.h> // offsetof()
#include "_iomodule.h"

Expand Down
3 changes: 2 additions & 1 deletion Modules/_io/textio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
1 change: 1 addition & 0 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
101 changes: 0 additions & 101 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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},
Expand Down
103 changes: 103 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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 */
};

Expand Down
6 changes: 6 additions & 0 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
@@ -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"


Expand Down
Loading

0 comments on commit 79823c1

Please sign in to comment.