Content-Length: 46696 | pFad | http://github.com/python/cpython/pull/111573.patch
thub.com
From d93f584855b7f983a21b5256a67629239b008b75 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Fri, 10 Nov 2023 16:05:42 -0700
Subject: [PATCH 1/4] Add more type info to _PyXI_excinfo.
---
Include/internal/pycore_crossinterp.h | 29 ++-
Modules/_xxsubinterpretersmodule.c | 2 +-
Python/crossinterp.c | 334 ++++++++++++++++----------
3 files changed, 230 insertions(+), 135 deletions(-)
diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h
index ee9ff0090c2484..c65b3379101ffc 100644
--- a/Include/internal/pycore_crossinterp.h
+++ b/Include/internal/pycore_crossinterp.h
@@ -170,9 +170,14 @@ extern void _PyXI_Fini(PyInterpreterState *interp);
// of the exception in the calling interpreter.
typedef struct _excinfo {
- const char *type;
+ struct _excinfo_type {
+ PyTypeObject *builtin;
+ const char *name;
+ const char *qualname;
+ const char *module;
+ } type;
const char *msg;
-} _Py_excinfo;
+} _PyXI_excinfo;
typedef enum error_code {
@@ -193,14 +198,16 @@ typedef struct _sharedexception {
// The kind of error to propagate.
_PyXI_errcode code;
// The exception information to propagate, if applicable.
- // This is populated only for _PyXI_ERR_UNCAUGHT_EXCEPTION.
- _Py_excinfo uncaught;
-} _PyXI_exception_info;
+ // This is populated only for some error codes,
+ // but always for _PyXI_ERR_UNCAUGHT_EXCEPTION.
+ _PyXI_excinfo uncaught;
+} _PyXI_error;
-PyAPI_FUNC(void) _PyXI_ApplyExceptionInfo(
- _PyXI_exception_info *info,
+PyAPI_FUNC(void) _PyXI_ApplyError(
+ _PyXI_error *err,
PyObject *exctype);
+
typedef struct xi_session _PyXI_session;
typedef struct _sharedns _PyXI_namespace;
@@ -251,13 +258,13 @@ struct xi_session {
// This is set if the interpreter is entered and raised an exception
// that needs to be handled in some special way during exit.
- _PyXI_errcode *exc_override;
+ _PyXI_errcode *error_override;
// This is set if exit captured an exception to propagate.
- _PyXI_exception_info *exc;
+ _PyXI_error *error;
// -- pre-allocated memory --
- _PyXI_exception_info _exc;
- _PyXI_errcode _exc_override;
+ _PyXI_error _error;
+ _PyXI_errcode _error_override;
};
PyAPI_FUNC(int) _PyXI_Enter(
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 001fa887847cbd..b52fdae64f7839 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -237,7 +237,7 @@ _run_in_interpreter(PyInterpreterState *interp,
// Prep and switch interpreters.
if (_PyXI_Enter(&session, interp, shareables) < 0) {
assert(!PyErr_Occurred());
- _PyXI_ApplyExceptionInfo(session.exc, excwrapper);
+ _PyXI_ApplyError(session.error, excwrapper);
assert(PyErr_Occurred());
return -1;
}
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
index a908f9ae340ee9..b90c3ece474700 100644
--- a/Python/crossinterp.c
+++ b/Python/crossinterp.c
@@ -7,6 +7,7 @@
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_pyerrors.h" // _PyErr_Clear()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
+#include "pycore_typeobject.h" // _PyType_GetModuleName()
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
@@ -564,6 +565,8 @@ _lookup_getdata_from_registry(PyInterpreterState *interp, PyObject *obj)
/* cross-interpreter data for builtin types */
+// bytes
+
struct _shared_bytes_data {
char *bytes;
Py_ssize_t len;
@@ -595,6 +598,8 @@ _bytes_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// str
+
struct _shared_str_data {
int kind;
const void *buffer;
@@ -626,6 +631,8 @@ _str_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// int
+
static PyObject *
_new_long_object(_PyCrossInterpreterData *data)
{
@@ -653,6 +660,8 @@ _long_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// float
+
static PyObject *
_new_float_object(_PyCrossInterpreterData *data)
{
@@ -676,6 +685,8 @@ _float_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// None
+
static PyObject *
_new_none_object(_PyCrossInterpreterData *data)
{
@@ -693,6 +704,8 @@ _none_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// bool
+
static PyObject *
_new_bool_object(_PyCrossInterpreterData *data)
{
@@ -713,6 +726,8 @@ _bool_shared(PyThreadState *tstate, PyObject *obj,
return 0;
}
+// tuple
+
struct _shared_tuple_data {
Py_ssize_t len;
_PyCrossInterpreterData **data;
@@ -806,6 +821,8 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj,
return -1;
}
+// registration
+
static void
_register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
{
@@ -898,17 +915,6 @@ _xidregistry_fini(struct _xidregistry *registry)
/* convenience utilities */
/*************************/
-static const char *
-_copy_raw_string(const char *str)
-{
- char *copied = PyMem_RawMalloc(strlen(str)+1);
- if (copied == NULL) {
- return NULL;
- }
- strcpy(copied, str);
- return copied;
-}
-
static const char *
_copy_string_obj_raw(PyObject *strobj)
{
@@ -944,115 +950,199 @@ _release_xid_data(_PyCrossInterpreterData *data, int rawfree)
}
+/***********************/
/* exception snapshots */
+/***********************/
static int
-_exc_type_name_as_utf8(PyObject *exc, const char **p_typename)
+_excinfo_init_type(struct _excinfo_type *info, PyObject *exc)
{
- // XXX Use PyObject_GetAttrString(Py_TYPE(exc), '__name__')?
- PyObject *nameobj = PyUnicode_FromString(Py_TYPE(exc)->tp_name);
- if (nameobj == NULL) {
- assert(PyErr_Occurred());
- *p_typename = "unable to format exception type name";
- return -1;
+ /* Note that this copies directly rather than into an intermediate
+ struct and does not clear on error. If we need that then we
+ should have a separate function to wrap this one
+ and do all that there. */
+ PyObject *strobj = NULL;
+
+ PyTypeObject *type = Py_TYPE(exc);
+ if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
+ assert(_Py_IsImmortal((PyObject *)type));
+ info->builtin = type;
}
- const char *name = PyUnicode_AsUTF8(nameobj);
- if (name == NULL) {
- assert(PyErr_Occurred());
- Py_DECREF(nameobj);
- *p_typename = "unable to encode exception type name";
+ else {
+ // Only builtin types are preserved.
+ info->builtin = NULL;
+ }
+
+ // __name__
+ strobj = PyType_GetName(type);
+ if (strobj == NULL) {
return -1;
}
- name = _copy_raw_string(name);
- Py_DECREF(nameobj);
- if (name == NULL) {
- *p_typename = "out of memory copying exception type name";
+ info->name = _copy_string_obj_raw(strobj);
+ Py_DECREF(strobj);
+ if (info->name == NULL) {
return -1;
}
- *p_typename = name;
- return 0;
-}
-static int
-_exc_msg_as_utf8(PyObject *exc, const char **p_msg)
-{
- PyObject *msgobj = PyObject_Str(exc);
- if (msgobj == NULL) {
- assert(PyErr_Occurred());
- *p_msg = "unable to format exception message";
+ // __qualname__
+ strobj = PyType_GetQualName(type);
+ if (strobj == NULL) {
return -1;
}
- const char *msg = PyUnicode_AsUTF8(msgobj);
- if (msg == NULL) {
- assert(PyErr_Occurred());
- Py_DECREF(msgobj);
- *p_msg = "unable to encode exception message";
+ info->qualname = _copy_string_obj_raw(strobj);
+ Py_DECREF(strobj);
+ if (info->name == NULL) {
return -1;
}
- msg = _copy_raw_string(msg);
- Py_DECREF(msgobj);
- if (msg == NULL) {
- assert(PyErr_ExceptionMatches(PyExc_MemoryError));
- *p_msg = "out of memory copying exception message";
+
+ // __module__
+ strobj = _PyType_GetModuleName(type);
+ if (strobj == NULL) {
+ return -1;
+ }
+ info->module = _copy_string_obj_raw(strobj);
+ Py_DECREF(strobj);
+ if (info->name == NULL) {
return -1;
}
- *p_msg = msg;
+
return 0;
}
static void
-_Py_excinfo_Clear(_Py_excinfo *info)
+_excinfo_clear_type(struct _excinfo_type *info)
{
- if (info->type != NULL) {
- PyMem_RawFree((void *)info->type);
+ if (info->builtin != NULL) {
+ assert(info->builtin->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
+ assert(_Py_IsImmortal((PyObject *)info->builtin));
+ }
+ if (info->name != NULL) {
+ PyMem_RawFree((void *)info->name);
+ }
+ if (info->qualname != NULL) {
+ PyMem_RawFree((void *)info->qualname);
+ }
+ if (info->module != NULL) {
+ PyMem_RawFree((void *)info->module);
+ }
+ *info = (struct _excinfo_type){NULL};
+}
+
+static void
+_excinfo_normalize_type(struct _excinfo_type *info,
+ const char **p_module, const char **p_qualname)
+{
+ if (info->name == NULL) {
+ assert(info->builtin == NULL);
+ assert(info->qualname == NULL);
+ assert(info->module == NULL);
+ // This is inspired by TracebackException.format_exception_only().
+ *p_module = NULL;
+ *p_qualname = NULL;
+ return;
}
+
+ const char *module = info->module;
+ const char *qualname = info->qualname;
+ if (qualname == NULL) {
+ qualname = info->name;
+ }
+ assert(module != NULL);
+ if (strcmp(module, "builtins") == 0) {
+ module = NULL;
+ }
+ else if (strcmp(module, "__main__") == 0) {
+ module = NULL;
+ }
+ *p_qualname = qualname;
+ *p_module = module;
+}
+
+static void
+_PyXI_excinfo_Clear(_PyXI_excinfo *info)
+{
+ _excinfo_clear_type(&info->type);
if (info->msg != NULL) {
PyMem_RawFree((void *)info->msg);
}
- *info = (_Py_excinfo){ NULL };
+ *info = (_PyXI_excinfo){{NULL}};
+}
+
+static PyObject *
+_PyXI_excinfo_format(_PyXI_excinfo *info)
+{
+ const char *module, *qualname;
+ _excinfo_normalize_type(&info->type, &module, &qualname);
+ if (qualname != NULL) {
+ if (module != NULL) {
+ if (info->msg != NULL) {
+ return PyUnicode_FromFormat("%s.%s: %s",
+ module, qualname, info->msg);
+ }
+ else {
+ return PyUnicode_FromFormat("%s.%s", module, qualname);
+ }
+ }
+ else {
+ if (info->msg != NULL) {
+ return PyUnicode_FromFormat("%s: %s", qualname, info->msg);
+ }
+ else {
+ return PyUnicode_FromString(qualname);
+ }
+ }
+ }
+ else if (info->msg != NULL) {
+ return PyUnicode_FromString(info->msg);
+ }
+ else {
+ Py_RETURN_NONE;
+ }
}
static const char *
-_Py_excinfo_InitFromException(_Py_excinfo *info, PyObject *exc)
+_PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc)
{
assert(exc != NULL);
- // Extract the exception type name.
- const char *typename = NULL;
- if (_exc_type_name_as_utf8(exc, &typename) < 0) {
- assert(typename != NULL);
- return typename;
+ if (PyErr_GivenExceptionMatches(exc, PyExc_MemoryError)) {
+ _PyXI_excinfo_Clear(info);
+ return NULL;
+ }
+ const char *failure = NULL;
+
+ if (_excinfo_init_type(&info->type, exc) < 0) {
+ failure = "error while initializing exception type snapshot";
+ goto error;
}
// Extract the exception message.
- const char *msg = NULL;
- if (_exc_msg_as_utf8(exc, &msg) < 0) {
- assert(msg != NULL);
- return msg;
+ PyObject *msgobj = PyObject_Str(exc);
+ if (msgobj == NULL) {
+ failure = "error while formatting exception";
+ goto error;
+ }
+ info->msg = _copy_string_obj_raw(msgobj);
+ Py_DECREF(msgobj);
+ if (info->msg == NULL) {
+ failure = "error while copying exception message";
+ goto error;
}
- info->type = typename;
- info->msg = msg;
return NULL;
+
+error:
+ assert(failure != NULL);
+ _PyXI_excinfo_Clear(info);
+ return failure;
}
static void
-_Py_excinfo_Apply(_Py_excinfo *info, PyObject *exctype)
+_PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
{
- if (info->type != NULL) {
- if (info->msg != NULL) {
- PyErr_Format(exctype, "%s: %s", info->type, info->msg);
- }
- else {
- PyErr_SetString(exctype, info->type);
- }
- }
- else if (info->msg != NULL) {
- PyErr_SetString(exctype, info->msg);
- }
- else {
- PyErr_SetNone(exctype);
- }
+ PyObject *formatted = _PyXI_excinfo_format(info);
+ PyErr_SetObject(exctype, formatted);
+ Py_DECREF(formatted);
}
@@ -1111,66 +1201,65 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
/* shared exceptions */
static const char *
-_PyXI_InitExceptionInfo(_PyXI_exception_info *info,
- PyObject *excobj, _PyXI_errcode code)
+_PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code)
{
- if (info->interp == NULL) {
- info->interp = PyInterpreterState_Get();
+ if (error->interp == NULL) {
+ error->interp = PyInterpreterState_Get();
}
const char *failure = NULL;
if (code == _PyXI_ERR_UNCAUGHT_EXCEPTION) {
// There is an unhandled exception we need to propagate.
- failure = _Py_excinfo_InitFromException(&info->uncaught, excobj);
+ failure = _PyXI_excinfo_InitFromException(&error->uncaught, excobj);
if (failure != NULL) {
- // We failed to initialize info->uncaught.
+ // We failed to initialize error->uncaught.
// XXX Print the excobj/traceback? Emit a warning?
// XXX Print the current exception/traceback?
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
- info->code = _PyXI_ERR_NO_MEMORY;
+ error->code = _PyXI_ERR_NO_MEMORY;
}
else {
- info->code = _PyXI_ERR_OTHER;
+ error->code = _PyXI_ERR_OTHER;
}
PyErr_Clear();
}
else {
- info->code = code;
+ error->code = code;
}
- assert(info->code != _PyXI_ERR_NO_ERROR);
+ assert(error->code != _PyXI_ERR_NO_ERROR);
}
else {
// There is an error code we need to propagate.
assert(excobj == NULL);
assert(code != _PyXI_ERR_NO_ERROR);
- info->code = code;
- _Py_excinfo_Clear(&info->uncaught);
+ error->code = code;
+ _PyXI_excinfo_Clear(&error->uncaught);
}
return failure;
}
void
-_PyXI_ApplyExceptionInfo(_PyXI_exception_info *info, PyObject *exctype)
+_PyXI_ApplyError(_PyXI_error *error, PyObject *exctype)
{
if (exctype == NULL) {
exctype = PyExc_RuntimeError;
}
- if (info->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) {
+ if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) {
// Raise an exception that proxies the propagated exception.
- _Py_excinfo_Apply(&info->uncaught, exctype);
+ _PyXI_excinfo_Apply(&error->uncaught, exctype);
}
- else if (info->code == _PyXI_ERR_NOT_SHAREABLE) {
+ else if (error->code == _PyXI_ERR_NOT_SHAREABLE) {
// Propagate the exception directly.
- _set_xid_lookup_failure(info->interp, NULL, info->uncaught.msg);
+ _set_xid_lookup_failure(error->interp, NULL, error->uncaught.msg);
}
else {
// Raise an exception corresponding to the code.
- assert(info->code != _PyXI_ERR_NO_ERROR);
- (void)_PyXI_ApplyErrorCode(info->code, info->interp);
- if (info->uncaught.type != NULL || info->uncaught.msg != NULL) {
+ assert(error->code != _PyXI_ERR_NO_ERROR);
+ (void)_PyXI_ApplyErrorCode(error->code, error->interp);
+ if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) {
// __context__ will be set to a proxy of the propagated exception.
PyObject *exc = PyErr_GetRaisedException();
- _Py_excinfo_Apply(&info->uncaught, exctype);
+ _PyXI_excinfo_Apply(&error->uncaught, exctype);
PyObject *exc2 = PyErr_GetRaisedException();
PyException_SetContext(exc, exc2);
PyErr_SetRaisedException(exc);
@@ -1603,7 +1692,7 @@ _PyXI_NamespaceFromDict(PyObject *nsobj, _PyXI_session *session)
error:
assert(PyErr_Occurred()
- || (session != NULL && session->exc_override != NULL));
+ || (session != NULL && session->error_override != NULL));
_sharedns_free(ns);
return NULL;
}
@@ -1637,9 +1726,9 @@ _enter_session(_PyXI_session *session, PyInterpreterState *interp)
assert(!session->running);
assert(session->main_ns == NULL);
// Set elsewhere and cleared in _capture_current_exception().
- assert(session->exc_override == NULL);
+ assert(session->error_override == NULL);
// Set elsewhere and cleared in _PyXI_ApplyCapturedException().
- assert(session->exc == NULL);
+ assert(session->error == NULL);
// Switch to interpreter.
PyThreadState *tstate = PyThreadState_Get();
@@ -1708,23 +1797,23 @@ _propagate_not_shareable_error(_PyXI_session *session)
PyInterpreterState *interp = _PyInterpreterState_GET();
if (PyErr_ExceptionMatches(_get_not_shareable_error_type(interp))) {
// We want to propagate the exception directly.
- session->_exc_override = _PyXI_ERR_NOT_SHAREABLE;
- session->exc_override = &session->_exc_override;
+ session->_error_override = _PyXI_ERR_NOT_SHAREABLE;
+ session->error_override = &session->_error_override;
}
}
static void
_capture_current_exception(_PyXI_session *session)
{
- assert(session->exc == NULL);
+ assert(session->error == NULL);
if (!PyErr_Occurred()) {
- assert(session->exc_override == NULL);
+ assert(session->error_override == NULL);
return;
}
// Handle the exception override.
- _PyXI_errcode *override = session->exc_override;
- session->exc_override = NULL;
+ _PyXI_errcode *override = session->error_override;
+ session->error_override = NULL;
_PyXI_errcode errcode = override != NULL
? *override
: _PyXI_ERR_UNCAUGHT_EXCEPTION;
@@ -1747,19 +1836,18 @@ _capture_current_exception(_PyXI_session *session)
}
// Capture the exception.
- _PyXI_exception_info *exc = &session->_exc;
- *exc = (_PyXI_exception_info){
+ _PyXI_error *err = &session->_error;
+ *err = (_PyXI_error){
.interp = session->init_tstate->interp,
};
const char *failure;
if (excval == NULL) {
- failure = _PyXI_InitExceptionInfo(exc, NULL, errcode);
+ failure = _PyXI_InitError(err, NULL, errcode);
}
else {
- failure = _PyXI_InitExceptionInfo(exc, excval,
- _PyXI_ERR_UNCAUGHT_EXCEPTION);
+ failure = _PyXI_InitError(err, excval, _PyXI_ERR_UNCAUGHT_EXCEPTION);
if (failure == NULL && override != NULL) {
- exc->code = errcode;
+ err->code = errcode;
}
}
@@ -1769,7 +1857,7 @@ _capture_current_exception(_PyXI_session *session)
fprintf(stderr,
"RunFailedError: script raised an uncaught exception (%s)",
failure);
- exc = NULL;
+ err = NULL;
}
// a temporary hack (famous last words)
@@ -1786,23 +1874,23 @@ _capture_current_exception(_PyXI_session *session)
// Finished!
assert(!PyErr_Occurred());
- session->exc = exc;
+ session->error = err;
}
void
_PyXI_ApplyCapturedException(_PyXI_session *session, PyObject *excwrapper)
{
assert(!PyErr_Occurred());
- assert(session->exc != NULL);
- _PyXI_ApplyExceptionInfo(session->exc, excwrapper);
+ assert(session->error != NULL);
+ _PyXI_ApplyError(session->error, excwrapper);
assert(PyErr_Occurred());
- session->exc = NULL;
+ session->error = NULL;
}
int
_PyXI_HasCapturedException(_PyXI_session *session)
{
- return session->exc != NULL;
+ return session->error != NULL;
}
int
@@ -1814,7 +1902,7 @@ _PyXI_Enter(_PyXI_session *session,
if (nsupdates != NULL) {
sharedns = _PyXI_NamespaceFromDict(nsupdates, NULL);
if (sharedns == NULL && PyErr_Occurred()) {
- assert(session->exc == NULL);
+ assert(session->error == NULL);
return -1;
}
}
@@ -1864,7 +1952,7 @@ _PyXI_Enter(_PyXI_session *session,
assert(PyErr_Occurred());
// We want to propagate all exceptions here directly (best effort).
assert(errcode != _PyXI_ERR_UNCAUGHT_EXCEPTION);
- session->exc_override = &errcode;
+ session->error_override = &errcode;
_capture_current_exception(session);
_exit_session(session);
if (sharedns != NULL) {
From 362412de1bb8479efd01f5076b9f698d58edd715 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Wed, 22 Nov 2023 16:51:22 -0700
Subject: [PATCH 2/4] Return a excinfo object from _PyXI_ApplyError().
---
Include/internal/pycore_crossinterp.h | 8 +-
Modules/_xxsubinterpretersmodule.c | 22 ++++-
Python/crossinterp.c | 132 +++++++++++++++++++++++---
3 files changed, 143 insertions(+), 19 deletions(-)
diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h
index c65b3379101ffc..ec9dac96292f35 100644
--- a/Include/internal/pycore_crossinterp.h
+++ b/Include/internal/pycore_crossinterp.h
@@ -203,9 +203,7 @@ typedef struct _sharedexception {
_PyXI_excinfo uncaught;
} _PyXI_error;
-PyAPI_FUNC(void) _PyXI_ApplyError(
- _PyXI_error *err,
- PyObject *exctype);
+PyAPI_FUNC(PyObject *) _PyXI_ApplyError(_PyXI_error *err);
typedef struct xi_session _PyXI_session;
@@ -273,9 +271,7 @@ PyAPI_FUNC(int) _PyXI_Enter(
PyObject *nsupdates);
PyAPI_FUNC(void) _PyXI_Exit(_PyXI_session *session);
-PyAPI_FUNC(void) _PyXI_ApplyCapturedException(
- _PyXI_session *session,
- PyObject *excwrapper);
+PyAPI_FUNC(PyObject *) _PyXI_ApplyCapturedException(_PyXI_session *session);
PyAPI_FUNC(int) _PyXI_HasCapturedException(_PyXI_session *session);
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index b52fdae64f7839..1c04a96cdd9205 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -225,6 +225,18 @@ _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
return 0;
}
+static void
+_raise_formatted(PyObject *exctype, PyObject *excinfo)
+{
+ PyObject *formatted = PyObject_GetAttrString(excinfo, "formatted");
+ if (formatted == NULL) {
+ assert(PyErr_Occurred());
+ return;
+ }
+ PyErr_SetObject(exctype, formatted);
+ Py_DECREF(formatted);
+}
+
static int
_run_in_interpreter(PyInterpreterState *interp,
const char *codestr, Py_ssize_t codestrlen,
@@ -237,7 +249,10 @@ _run_in_interpreter(PyInterpreterState *interp,
// Prep and switch interpreters.
if (_PyXI_Enter(&session, interp, shareables) < 0) {
assert(!PyErr_Occurred());
- _PyXI_ApplyError(session.error, excwrapper);
+ PyObject *excinfo = _PyXI_ApplyError(session.error);
+ if (excinfo != NULL) {
+ _raise_formatted(excwrapper, excinfo);
+ }
assert(PyErr_Occurred());
return -1;
}
@@ -251,7 +266,10 @@ _run_in_interpreter(PyInterpreterState *interp,
// Propagate any exception out to the caller.
assert(!PyErr_Occurred());
if (res < 0) {
- _PyXI_ApplyCapturedException(&session, excwrapper);
+ PyObject *excinfo = _PyXI_ApplyCapturedException(&session);
+ if (excinfo != NULL) {
+ _raise_formatted(excwrapper, excinfo);
+ }
}
else {
assert(!_PyXI_HasCapturedException(&session));
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
index b90c3ece474700..21b96ef05ed799 100644
--- a/Python/crossinterp.c
+++ b/Python/crossinterp.c
@@ -5,6 +5,7 @@
#include "pycore_ceval.h" // _Py_simple_func
#include "pycore_crossinterp.h" // struct _xid
#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_namespace.h" //_PyNamespace_New()
#include "pycore_pyerrors.h" // _PyErr_Clear()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_typeobject.h" // _PyType_GetModuleName()
@@ -1145,6 +1146,116 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
Py_DECREF(formatted);
}
+static PyObject *
+_PyXI_excinfo_TypeAsObject(_PyXI_excinfo *info)
+{
+ PyObject *ns = _PyNamespace_New(NULL);
+ if (ns == NULL) {
+ return NULL;
+ }
+ int empty = 1;
+
+ if (info->type.name != NULL) {
+ PyObject *name = PyUnicode_FromString(info->type.name);
+ if (name == NULL) {
+ goto error;
+ }
+ int res = PyObject_SetAttrString(ns, "__name__", name);
+ Py_DECREF(name);
+ if (res < 0) {
+ goto error;
+ }
+ empty = 0;
+ }
+
+ if (info->type.qualname != NULL) {
+ PyObject *qualname = PyUnicode_FromString(info->type.qualname);
+ if (qualname == NULL) {
+ goto error;
+ }
+ int res = PyObject_SetAttrString(ns, "__qualname__", qualname);
+ Py_DECREF(qualname);
+ if (res < 0) {
+ goto error;
+ }
+ empty = 0;
+ }
+
+ if (info->type.module != NULL) {
+ PyObject *module = PyUnicode_FromString(info->type.module);
+ if (module == NULL) {
+ goto error;
+ }
+ int res = PyObject_SetAttrString(ns, "__module__", module);
+ Py_DECREF(module);
+ if (res < 0) {
+ goto error;
+ }
+ empty = 0;
+ }
+
+ if (empty) {
+ Py_CLEAR(ns);
+ }
+
+ return ns;
+
+error:
+ Py_DECREF(ns);
+ return NULL;
+}
+
+static PyObject *
+_PyXI_excinfo_AsObject(_PyXI_excinfo *info)
+{
+ PyObject *ns = _PyNamespace_New(NULL);
+ if (ns == NULL) {
+ return NULL;
+ }
+ int res;
+
+ PyObject *type = _PyXI_excinfo_TypeAsObject(info);
+ if (type == NULL) {
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ type = Py_NewRef(Py_None);
+ }
+ res = PyObject_SetAttrString(ns, "type", type);
+ Py_DECREF(type);
+ if (res < 0) {
+ goto error;
+ }
+
+ PyObject *msg = info->msg != NULL
+ ? PyUnicode_FromString(info->msg)
+ : Py_NewRef(Py_None);
+ if (msg == NULL) {
+ goto error;
+ }
+ res = PyObject_SetAttrString(ns, "msg", msg);
+ Py_DECREF(msg);
+ if (res < 0) {
+ goto error;
+ }
+
+ PyObject *formatted = _PyXI_excinfo_format(info);
+ if (formatted == NULL) {
+ goto error;
+ }
+ res = PyObject_SetAttrString(ns, "formatted", formatted);
+ Py_DECREF(formatted);
+ if (res < 0) {
+ goto error;
+ }
+
+ return ns;
+
+error:
+ Py_DECREF(ns);
+ return NULL;
+}
+
/***************************/
/* short-term data sharing */
@@ -1238,15 +1349,12 @@ _PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code)
return failure;
}
-void
-_PyXI_ApplyError(_PyXI_error *error, PyObject *exctype)
+PyObject *
+_PyXI_ApplyError(_PyXI_error *error)
{
- if (exctype == NULL) {
- exctype = PyExc_RuntimeError;
- }
if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) {
// Raise an exception that proxies the propagated exception.
- _PyXI_excinfo_Apply(&error->uncaught, exctype);
+ return _PyXI_excinfo_AsObject(&error->uncaught);
}
else if (error->code == _PyXI_ERR_NOT_SHAREABLE) {
// Propagate the exception directly.
@@ -1259,13 +1367,14 @@ _PyXI_ApplyError(_PyXI_error *error, PyObject *exctype)
if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) {
// __context__ will be set to a proxy of the propagated exception.
PyObject *exc = PyErr_GetRaisedException();
- _PyXI_excinfo_Apply(&error->uncaught, exctype);
+ _PyXI_excinfo_Apply(&error->uncaught, PyExc_RuntimeError);
PyObject *exc2 = PyErr_GetRaisedException();
PyException_SetContext(exc, exc2);
PyErr_SetRaisedException(exc);
}
}
assert(PyErr_Occurred());
+ return NULL;
}
/* shared namespaces */
@@ -1877,14 +1986,15 @@ _capture_current_exception(_PyXI_session *session)
session->error = err;
}
-void
-_PyXI_ApplyCapturedException(_PyXI_session *session, PyObject *excwrapper)
+PyObject *
+_PyXI_ApplyCapturedException(_PyXI_session *session)
{
assert(!PyErr_Occurred());
assert(session->error != NULL);
- _PyXI_ApplyError(session->error, excwrapper);
- assert(PyErr_Occurred());
+ PyObject *res = _PyXI_ApplyError(session->error);
+ assert((res == NULL) != (PyErr_Occurred() == NULL));
session->error = NULL;
+ return res;
}
int
From 61845dd695b44dbcc8db6e959996979f102cf779 Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Wed, 22 Nov 2023 17:12:43 -0700
Subject: [PATCH 3/4] Return an excinfo object from _interpreters.exec().
---
Lib/test/support/interpreters.py | 6 +++-
Lib/test/test__xxinterpchannels.py | 12 ++++----
Lib/test/test__xxsubinterpreters.py | 14 +++++----
Lib/test/test_import/__init__.py | 10 ++++---
Lib/test/test_importlib/test_util.py | 22 ++++++--------
Modules/_xxsubinterpretersmodule.c | 43 ++++++++++++----------------
6 files changed, 51 insertions(+), 56 deletions(-)
diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py
index ab9342b767dfae..5fd3a787d5b29d 100644
--- a/Lib/test/support/interpreters.py
+++ b/Lib/test/support/interpreters.py
@@ -110,7 +110,11 @@ def run(self, src_str, /, channels=None):
that time, the previous interpreter is allowed to run
in other threads.
"""
- _interpreters.exec(self._id, src_str, channels)
+ err = _interpreters.exec(self._id, src_str, channels)
+ if err is not None:
+ exc = RunFailedError(err.formatted)
+ exc.snapshot = err
+ raise exc
def create_channel():
diff --git a/Lib/test/test__xxinterpchannels.py b/Lib/test/test__xxinterpchannels.py
index 1c1ef3fac9d65f..2b75e2f1916c82 100644
--- a/Lib/test/test__xxinterpchannels.py
+++ b/Lib/test/test__xxinterpchannels.py
@@ -1017,16 +1017,16 @@ def test_close_multiple_users(self):
_channels.recv({cid})
"""))
channels.close(cid)
- with self.assertRaises(interpreters.RunFailedError) as cm:
- interpreters.run_string(id1, dedent(f"""
+
+ excsnap = interpreters.run_string(id1, dedent(f"""
_channels.send({cid}, b'spam')
"""))
- self.assertIn('ChannelClosedError', str(cm.exception))
- with self.assertRaises(interpreters.RunFailedError) as cm:
- interpreters.run_string(id2, dedent(f"""
+ self.assertEqual(excsnap.type.__name__, 'ChannelClosedError')
+
+ excsnap = interpreters.run_string(id2, dedent(f"""
_channels.send({cid}, b'spam')
"""))
- self.assertIn('ChannelClosedError', str(cm.exception))
+ self.assertEqual(excsnap.type.__name__, 'ChannelClosedError')
def test_close_multiple_times(self):
cid = channels.create()
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py
index d2056c9f52287b..ef2b65cb8d5638 100644
--- a/Lib/test/test__xxsubinterpreters.py
+++ b/Lib/test/test__xxsubinterpreters.py
@@ -956,11 +956,12 @@ class NeverError(Exception): pass
raise NeverError # never raised
""").format(dedent(text))
if fails:
- with self.assertRaises(excwrapper) as caught:
- interpreters.run_string(self.id, script)
- return caught.exception
+ err = interpreters.run_string(self.id, script)
+ self.assertIsNot(err, None)
+ return err
else:
- interpreters.run_string(self.id, script)
+ err = interpreters.run_string(self.id, script)
+ self.assertIs(err, None)
return None
except:
raise # re-raise
@@ -982,11 +983,12 @@ def _assert_run_failed(self, exctype, msg, script):
exc = self.run_script(script, fails=True)
# Check the wrapper exception.
+ self.assertEqual(exc.type.__name__, exctype_name)
if msg is None:
- self.assertEqual(str(exc).split(':')[0],
+ self.assertEqual(exc.formatted.split(':')[0],
exctype_name)
else:
- self.assertEqual(str(exc),
+ self.assertEqual(exc.formatted,
'{}: {}'.format(exctype_name, msg))
return exc
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index aa465c70dfbcd0..1ecac4f37fe1c1 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -1968,10 +1968,12 @@ def test_disallowed_reimport(self):
print(_testsinglephase)
''')
interpid = _interpreters.create()
- with self.assertRaises(_interpreters.RunFailedError):
- _interpreters.run_string(interpid, script)
- with self.assertRaises(_interpreters.RunFailedError):
- _interpreters.run_string(interpid, script)
+
+ excsnap = _interpreters.run_string(interpid, script)
+ self.assertIsNot(excsnap, None)
+
+ excsnap = _interpreters.run_string(interpid, script)
+ self.assertIsNot(excsnap, None)
class TestSinglePhaseSnapshot(ModuleSnapshot):
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index 5da72a21f586ee..914176559806f4 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -655,25 +655,19 @@ def test_magic_number(self):
@unittest.skipIf(_interpreters is None, 'subinterpreters required')
class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):
- ERROR = re.compile("^ImportError: module (.*) does not support loading in subinterpreters")
-
def run_with_own_gil(self, script):
interpid = _interpreters.create(isolated=True)
- try:
- _interpreters.run_string(interpid, script)
- except _interpreters.RunFailedError as exc:
- if m := self.ERROR.match(str(exc)):
- modname, = m.groups()
- raise ImportError(modname)
+ excsnap = _interpreters.run_string(interpid, script)
+ if excsnap is not None:
+ if excsnap.type.__name__ == 'ImportError':
+ raise ImportError(excsnap.msg)
def run_with_shared_gil(self, script):
interpid = _interpreters.create(isolated=False)
- try:
- _interpreters.run_string(interpid, script)
- except _interpreters.RunFailedError as exc:
- if m := self.ERROR.match(str(exc)):
- modname, = m.groups()
- raise ImportError(modname)
+ excsnap = _interpreters.run_string(interpid, script)
+ if excsnap is not None:
+ if excsnap.type.__name__ == 'ImportError':
+ raise ImportError(excsnap.msg)
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
def test_single_phase_init_module(self):
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index 1c04a96cdd9205..eea3ae9f2a7acf 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -225,23 +225,11 @@ _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
return 0;
}
-static void
-_raise_formatted(PyObject *exctype, PyObject *excinfo)
-{
- PyObject *formatted = PyObject_GetAttrString(excinfo, "formatted");
- if (formatted == NULL) {
- assert(PyErr_Occurred());
- return;
- }
- PyErr_SetObject(exctype, formatted);
- Py_DECREF(formatted);
-}
-
static int
_run_in_interpreter(PyInterpreterState *interp,
const char *codestr, Py_ssize_t codestrlen,
PyObject *shareables, int flags,
- PyObject *excwrapper)
+ PyObject **p_excinfo)
{
assert(!PyErr_Occurred());
_PyXI_session session = {0};
@@ -251,7 +239,7 @@ _run_in_interpreter(PyInterpreterState *interp,
assert(!PyErr_Occurred());
PyObject *excinfo = _PyXI_ApplyError(session.error);
if (excinfo != NULL) {
- _raise_formatted(excwrapper, excinfo);
+ *p_excinfo = excinfo;
}
assert(PyErr_Occurred());
return -1;
@@ -268,7 +256,7 @@ _run_in_interpreter(PyInterpreterState *interp,
if (res < 0) {
PyObject *excinfo = _PyXI_ApplyCapturedException(&session);
if (excinfo != NULL) {
- _raise_formatted(excwrapper, excinfo);
+ *p_excinfo = excinfo;
}
}
else {
@@ -539,7 +527,8 @@ convert_code_arg(PyObject *arg, const char *fname, const char *displayname,
static int
_interp_exec(PyObject *self,
- PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg)
+ PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg,
+ PyObject **p_excinfo)
{
// Look up the interpreter.
PyInterpreterState *interp = PyInterpreterID_LookUp(id_arg);
@@ -558,10 +547,8 @@ _interp_exec(PyObject *self,
}
// Run the code in the interpreter.
- module_state *state = get_module_state(self);
- assert(state != NULL);
int res = _run_in_interpreter(interp, codestr, codestrlen,
- shared_arg, flags, state->RunFailedError);
+ shared_arg, flags, p_excinfo);
Py_XDECREF(bytes_obj);
if (res < 0) {
return -1;
@@ -595,10 +582,12 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
- int res = _interp_exec(self, id, code, shared);
+ PyObject *excinfo = NULL;
+ int res = _interp_exec(self, id, code, shared, &excinfo);
Py_DECREF(code);
if (res < 0) {
- return NULL;
+ assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
+ return excinfo;
}
Py_RETURN_NONE;
}
@@ -638,10 +627,12 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
- int res = _interp_exec(self, id, script, shared);
+ PyObject *excinfo = NULL;
+ int res = _interp_exec(self, id, script, shared, &excinfo);
Py_DECREF(script);
if (res < 0) {
- return NULL;
+ assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
+ return excinfo;
}
Py_RETURN_NONE;
}
@@ -672,10 +663,12 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
}
- int res = _interp_exec(self, id, (PyObject *)code, shared);
+ PyObject *excinfo = NULL;
+ int res = _interp_exec(self, id, (PyObject *)code, shared, &excinfo);
Py_DECREF(code);
if (res < 0) {
- return NULL;
+ assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
+ return excinfo;
}
Py_RETURN_NONE;
}
From dfff3e30ffdf25511d472385bf804bb15f71173e Mon Sep 17 00:00:00 2001
From: Eric Snow
Date: Fri, 22 Sep 2023 13:18:55 -0600
Subject: [PATCH 4/4] _interpreters.RunFailedError ->
interpreters.RunFailedError
---
Lib/test/support/interpreters.py | 22 ++++++++---
Lib/test/test__xxsubinterpreters.py | 11 +++---
Lib/test/test_interpreters.py | 5 +++
Modules/_xxsubinterpretersmodule.c | 57 +----------------------------
4 files changed, 28 insertions(+), 67 deletions(-)
diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py
index 5fd3a787d5b29d..089fe7ef56df78 100644
--- a/Lib/test/support/interpreters.py
+++ b/Lib/test/support/interpreters.py
@@ -14,6 +14,7 @@
__all__ = [
'Interpreter', 'get_current', 'get_main', 'create', 'list_all',
+ 'RunFailedError',
'SendChannel', 'RecvChannel',
'create_channel', 'list_all_channels', 'is_shareable',
'ChannelError', 'ChannelNotFoundError',
@@ -21,6 +22,19 @@
]
+class RunFailedError(RuntimeError):
+
+ def __init__(self, excinfo):
+ msg = excinfo.formatted
+ if not msg:
+ if excinfo.type and snapshot.msg:
+ msg = f'{snapshot.type.__name__}: {snapshot.msg}'
+ else:
+ msg = snapshot.type.__name__ or snapshot.msg
+ super().__init__(msg)
+ self.snapshot = excinfo
+
+
def create(*, isolated=True):
"""Return a new (idle) Python interpreter."""
id = _interpreters.create(isolated=isolated)
@@ -110,11 +124,9 @@ def run(self, src_str, /, channels=None):
that time, the previous interpreter is allowed to run
in other threads.
"""
- err = _interpreters.exec(self._id, src_str, channels)
- if err is not None:
- exc = RunFailedError(err.formatted)
- exc.snapshot = err
- raise exc
+ excinfo = _interpreters.exec(self._id, src_str, channels)
+ if excinfo is not None:
+ raise RunFailedError(excinfo)
def create_channel():
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py
index ef2b65cb8d5638..64a9db95e5eaf5 100644
--- a/Lib/test/test__xxsubinterpreters.py
+++ b/Lib/test/test__xxsubinterpreters.py
@@ -940,7 +940,6 @@ def add_module(self, modname, text):
return script_helper.make_script(tempdir, modname, text)
def run_script(self, text, *, fails=False):
- excwrapper = interpreters.RunFailedError
r, w = os.pipe()
try:
script = dedent(f"""
@@ -980,18 +979,18 @@ def _assert_run_failed(self, exctype, msg, script):
exctype_name = exctype.__name__
# Run the script.
- exc = self.run_script(script, fails=True)
+ excinfo = self.run_script(script, fails=True)
# Check the wrapper exception.
- self.assertEqual(exc.type.__name__, exctype_name)
+ self.assertEqual(excinfo.type.__name__, exctype_name)
if msg is None:
- self.assertEqual(exc.formatted.split(':')[0],
+ self.assertEqual(excinfo.formatted.split(':')[0],
exctype_name)
else:
- self.assertEqual(exc.formatted,
+ self.assertEqual(excinfo.formatted,
'{}: {}'.format(exctype_name, msg))
- return exc
+ return excinfo
def assert_run_failed(self, exctype, script):
self._assert_run_failed(exctype, None, script)
diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py
index 7c030bcf0321cd..5663706c0ccfb7 100644
--- a/Lib/test/test_interpreters.py
+++ b/Lib/test/test_interpreters.py
@@ -478,6 +478,11 @@ def test_success(self):
self.assertEqual(out, 'it worked!')
+ def test_failure(self):
+ interp = interpreters.create()
+ with self.assertRaises(interpreters.RunFailedError):
+ interp.run('raise Exception')
+
def test_in_thread(self):
interp = interpreters.create()
script, file = _captured_script('print("it worked!", end="")')
diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c
index eea3ae9f2a7acf..02c2abed27ddfa 100644
--- a/Modules/_xxsubinterpretersmodule.c
+++ b/Modules/_xxsubinterpretersmodule.c
@@ -28,31 +28,11 @@ _get_current_interp(void)
return PyInterpreterState_Get();
}
-static PyObject *
-add_new_exception(PyObject *mod, const char *name, PyObject *base)
-{
- assert(!PyObject_HasAttrStringWithError(mod, name));
- PyObject *exctype = PyErr_NewException(name, base, NULL);
- if (exctype == NULL) {
- return NULL;
- }
- int res = PyModule_AddType(mod, (PyTypeObject *)exctype);
- if (res < 0) {
- Py_DECREF(exctype);
- return NULL;
- }
- return exctype;
-}
-
-#define ADD_NEW_EXCEPTION(MOD, NAME, BASE) \
- add_new_exception(MOD, MODULE_NAME "." Py_STRINGIFY(NAME), BASE)
-
/* module state *************************************************************/
typedef struct {
- /* exceptions */
- PyObject *RunFailedError;
+ int _notused;
} module_state;
static inline module_state *
@@ -67,18 +47,12 @@ get_module_state(PyObject *mod)
static int
traverse_module_state(module_state *state, visitproc visit, void *arg)
{
- /* exceptions */
- Py_VISIT(state->RunFailedError);
-
return 0;
}
static int
clear_module_state(module_state *state)
{
- /* exceptions */
- Py_CLEAR(state->RunFailedError);
-
return 0;
}
@@ -177,30 +151,6 @@ get_code_str(PyObject *arg, Py_ssize_t *len_p, PyObject **bytes_p, int *flags_p)
/* interpreter-specific code ************************************************/
-static int
-exceptions_init(PyObject *mod)
-{
- module_state *state = get_module_state(mod);
- if (state == NULL) {
- return -1;
- }
-
-#define ADD(NAME, BASE) \
- do { \
- assert(state->NAME == NULL); \
- state->NAME = ADD_NEW_EXCEPTION(mod, NAME, BASE); \
- if (state->NAME == NULL) { \
- return -1; \
- } \
- } while (0)
-
- // An uncaught exception came out of interp_run_string().
- ADD(RunFailedError, PyExc_RuntimeError);
-#undef ADD
-
- return 0;
-}
-
static int
_run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
{
@@ -770,11 +720,6 @@ The 'interpreters' module provides a more convenient interface.");
static int
module_exec(PyObject *mod)
{
- /* Add exception types */
- if (exceptions_init(mod) != 0) {
- goto error;
- }
-
// PyInterpreterID
if (PyModule_AddType(mod, &PyInterpreterID_Type) < 0) {
goto error;
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/python/cpython/pull/111573.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy