gh-132781: Cleanup Code Related to NotShareableError (gh-132782)

The following are added to the internal C-API:

* _PyErr_FormatV()
* _PyErr_SetModuleNotFoundError()
* _PyXIData_GetNotShareableErrorType()
* _PyXIData_FormatNotShareableError()

We also drop _PyXIData_lookup_context_t and _PyXIData_GetLookupContext().
This commit is contained in:
Eric Snow 2025-04-25 14:43:38 -06:00 committed by GitHub
parent 4c20f46fa0
commit cd9536a087
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 322 additions and 177 deletions

View file

@ -64,7 +64,8 @@ _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp,
static void xid_lookup_init(_PyXIData_lookup_t *);
static void xid_lookup_fini(_PyXIData_lookup_t *);
static xidatafunc lookup_getdata(_PyXIData_lookup_context_t *, PyObject *);
struct _dlcontext;
static xidatafunc lookup_getdata(struct _dlcontext *, PyObject *);
#include "crossinterp_data_lookup.h"
@ -198,31 +199,34 @@ _check_xidata(PyThreadState *tstate, _PyXIData_t *data)
}
static inline void
_set_xid_lookup_failure(dlcontext_t *ctx, PyObject *obj, const char *msg)
_set_xid_lookup_failure(PyThreadState *tstate, PyObject *obj, const char *msg,
PyObject *cause)
{
PyObject *exctype = ctx->PyExc_NotShareableError;
assert(exctype != NULL);
if (msg != NULL) {
assert(obj == NULL);
PyErr_SetString(exctype, msg);
set_notshareableerror(tstate, cause, 0, msg);
}
else if (obj == NULL) {
PyErr_SetString(exctype,
"object does not support cross-interpreter data");
msg = "object does not support cross-interpreter data";
set_notshareableerror(tstate, cause, 0, msg);
}
else {
PyErr_Format(exctype,
"%S does not support cross-interpreter data", obj);
msg = "%S does not support cross-interpreter data";
format_notshareableerror(tstate, cause, 0, msg, obj);
}
}
int
_PyObject_CheckXIData(_PyXIData_lookup_context_t *ctx, PyObject *obj)
_PyObject_CheckXIData(PyThreadState *tstate, PyObject *obj)
{
xidatafunc getdata = lookup_getdata(ctx, obj);
dlcontext_t ctx;
if (get_lookup_context(tstate, &ctx) < 0) {
return -1;
}
xidatafunc getdata = lookup_getdata(&ctx, obj);
if (getdata == NULL) {
if (!PyErr_Occurred()) {
_set_xid_lookup_failure(ctx, obj, NULL);
if (!_PyErr_Occurred(tstate)) {
_set_xid_lookup_failure(tstate, obj, NULL, NULL);
}
return -1;
}
@ -230,10 +234,9 @@ _PyObject_CheckXIData(_PyXIData_lookup_context_t *ctx, PyObject *obj)
}
int
_PyObject_GetXIData(_PyXIData_lookup_context_t *ctx,
_PyObject_GetXIData(PyThreadState *tstate,
PyObject *obj, _PyXIData_t *data)
{
PyThreadState *tstate = PyThreadState_Get();
PyInterpreterState *interp = tstate->interp;
// Reset data before re-populating.
@ -241,18 +244,26 @@ _PyObject_GetXIData(_PyXIData_lookup_context_t *ctx,
_PyXIData_INTERPID(data) = -1;
// Call the "getdata" func for the object.
dlcontext_t ctx;
if (get_lookup_context(tstate, &ctx) < 0) {
return -1;
}
Py_INCREF(obj);
xidatafunc getdata = lookup_getdata(ctx, obj);
xidatafunc getdata = lookup_getdata(&ctx, obj);
if (getdata == NULL) {
Py_DECREF(obj);
if (!PyErr_Occurred()) {
_set_xid_lookup_failure(ctx, obj, NULL);
if (!_PyErr_Occurred(tstate)) {
_set_xid_lookup_failure(tstate, obj, NULL, NULL);
}
return -1;
}
int res = getdata(tstate, obj, data);
Py_DECREF(obj);
if (res != 0) {
PyObject *cause = _PyErr_GetRaisedException(tstate);
assert(cause != NULL);
_set_xid_lookup_failure(tstate, obj, NULL, cause);
Py_XDECREF(cause);
return -1;
}
@ -966,7 +977,7 @@ _PyXI_ClearExcInfo(_PyXI_excinfo *info)
static int
_PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
{
dlcontext_t ctx;
PyThreadState *tstate = _PyThreadState_GET();
assert(!PyErr_Occurred());
switch (code) {
@ -997,10 +1008,7 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
"failed to apply namespace to __main__");
break;
case _PyXI_ERR_NOT_SHAREABLE:
if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
return -1;
}
_set_xid_lookup_failure(&ctx, NULL, NULL);
_set_xid_lookup_failure(tstate, NULL, NULL, NULL);
break;
default:
#ifdef Py_DEBUG
@ -1056,17 +1064,15 @@ _PyXI_InitError(_PyXI_error *error, PyObject *excobj, _PyXI_errcode code)
PyObject *
_PyXI_ApplyError(_PyXI_error *error)
{
PyThreadState *tstate = PyThreadState_Get();
if (error->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) {
// Raise an exception that proxies the propagated exception.
return _PyXI_excinfo_AsObject(&error->uncaught);
}
else if (error->code == _PyXI_ERR_NOT_SHAREABLE) {
// Propagate the exception directly.
dlcontext_t ctx;
if (_PyXIData_GetLookupContext(error->interp, &ctx) < 0) {
return NULL;
}
_set_xid_lookup_failure(&ctx, NULL, error->uncaught.msg);
assert(!_PyErr_Occurred(tstate));
_set_xid_lookup_failure(tstate, NULL, error->uncaught.msg, NULL);
}
else {
// Raise an exception corresponding to the code.
@ -1153,12 +1159,8 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
PyErr_NoMemory();
return -1;
}
PyInterpreterState *interp = PyInterpreterState_Get();
dlcontext_t ctx;
if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
return -1;
}
if (_PyObject_GetXIData(&ctx, value, item->data) != 0) {
PyThreadState *tstate = PyThreadState_Get();
if (_PyObject_GetXIData(tstate, value, item->data) != 0) {
PyMem_RawFree(item->data);
item->data = NULL;
// The caller may want to propagate PyExc_NotShareableError
@ -1615,14 +1617,14 @@ _propagate_not_shareable_error(_PyXI_session *session)
if (session == NULL) {
return;
}
PyInterpreterState *interp = PyInterpreterState_Get();
dlcontext_t ctx;
if (_PyXIData_GetLookupContext(interp, &ctx) < 0) {
PyThreadState *tstate = PyThreadState_Get();
PyObject *exctype = get_notshareableerror_type(tstate);
if (exctype == NULL) {
PyErr_FormatUnraisable(
"Exception ignored while propagating not shareable error");
return;
}
if (PyErr_ExceptionMatches(ctx.PyExc_NotShareableError)) {
if (PyErr_ExceptionMatches(exctype)) {
// We want to propagate the exception directly.
session->_error_override = _PyXI_ERR_NOT_SHAREABLE;
session->error_override = &session->_error_override;

View file

@ -1,7 +1,10 @@
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
typedef _PyXIData_lookup_context_t dlcontext_t;
typedef struct _dlcontext {
_PyXIData_lookup_t *global;
_PyXIData_lookup_t *local;
} dlcontext_t;
typedef _PyXIData_registry_t dlregistry_t;
typedef _PyXIData_regitem_t dlregitem_t;
@ -26,6 +29,26 @@ xid_lookup_fini(_PyXIData_lookup_t *state)
_xidregistry_fini(&state->registry);
}
static int
get_lookup_context(PyThreadState *tstate, dlcontext_t *res)
{
_PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(tstate->interp);
if (global == NULL) {
assert(PyErr_Occurred());
return -1;
}
_PyXI_state_t *local = _PyXI_GET_STATE(tstate->interp);
if (local == NULL) {
assert(PyErr_Occurred());
return -1;
}
*res = (dlcontext_t){
.global = &global->data_lookup,
.local = &local->data_lookup,
};
return 0;
}
static xidatafunc
lookup_getdata(dlcontext_t *ctx, PyObject *obj)
{
@ -38,32 +61,41 @@ lookup_getdata(dlcontext_t *ctx, PyObject *obj)
/* exported API */
int
_PyXIData_GetLookupContext(PyInterpreterState *interp,
_PyXIData_lookup_context_t *res)
PyObject *
_PyXIData_GetNotShareableErrorType(PyThreadState *tstate)
{
_PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(interp);
if (global == NULL) {
assert(PyErr_Occurred());
return -1;
}
_PyXI_state_t *local = _PyXI_GET_STATE(interp);
if (local == NULL) {
assert(PyErr_Occurred());
return -1;
}
*res = (dlcontext_t){
.global = &global->data_lookup,
.local = &local->data_lookup,
.PyExc_NotShareableError = local->exceptions.PyExc_NotShareableError,
};
return 0;
PyObject *exctype = get_notshareableerror_type(tstate);
assert(exctype != NULL);
return exctype;
}
xidatafunc
_PyXIData_Lookup(_PyXIData_lookup_context_t *ctx, PyObject *obj)
void
_PyXIData_SetNotShareableError(PyThreadState *tstate, const char *msg)
{
return lookup_getdata(ctx, obj);
PyObject *cause = NULL;
set_notshareableerror(tstate, cause, 1, msg);
}
void
_PyXIData_FormatNotShareableError(PyThreadState *tstate,
const char *format, ...)
{
PyObject *cause = NULL;
va_list vargs;
va_start(vargs, format);
format_notshareableerror_v(tstate, cause, 1, format, vargs);
va_end(vargs);
}
xidatafunc
_PyXIData_Lookup(PyThreadState *tstate, PyObject *obj)
{
dlcontext_t ctx;
if (get_lookup_context(tstate, &ctx) < 0) {
return NULL;
}
return lookup_getdata(&ctx, obj);
}
@ -250,7 +282,7 @@ _xidregistry_clear(dlregistry_t *xidregistry)
}
int
_PyXIData_RegisterClass(_PyXIData_lookup_context_t *ctx,
_PyXIData_RegisterClass(PyThreadState *tstate,
PyTypeObject *cls, xidatafunc getdata)
{
if (!PyType_Check(cls)) {
@ -263,7 +295,11 @@ _PyXIData_RegisterClass(_PyXIData_lookup_context_t *ctx,
}
int res = 0;
dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
dlcontext_t ctx;
if (get_lookup_context(tstate, &ctx) < 0) {
return -1;
}
dlregistry_t *xidregistry = _get_xidregistry_for_type(&ctx, cls);
_xidregistry_lock(xidregistry);
dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@ -281,10 +317,14 @@ finally:
}
int
_PyXIData_UnregisterClass(_PyXIData_lookup_context_t *ctx, PyTypeObject *cls)
_PyXIData_UnregisterClass(PyThreadState *tstate, PyTypeObject *cls)
{
int res = 0;
dlregistry_t *xidregistry = _get_xidregistry_for_type(ctx, cls);
dlcontext_t ctx;
if (get_lookup_context(tstate, &ctx) < 0) {
return -1;
}
dlregistry_t *xidregistry = _get_xidregistry_for_type(&ctx, cls);
_xidregistry_lock(xidregistry);
dlregitem_t *matched = _xidregistry_find_type(xidregistry, cls);
@ -508,11 +548,6 @@ _tuple_shared_free(void* data)
static int
_tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
{
dlcontext_t ctx;
if (_PyXIData_GetLookupContext(tstate->interp, &ctx) < 0) {
return -1;
}
Py_ssize_t len = PyTuple_GET_SIZE(obj);
if (len < 0) {
return -1;
@ -539,7 +574,7 @@ _tuple_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *data)
int res = -1;
if (!_Py_EnterRecursiveCallTstate(tstate, " while sharing a tuple")) {
res = _PyObject_GetXIData(&ctx, item, data);
res = _PyObject_GetXIData(tstate, item, data);
_Py_LeaveRecursiveCallTstate(tstate);
}
if (res < 0) {

View file

@ -1,4 +1,25 @@
static void
_ensure_current_cause(PyThreadState *tstate, PyObject *cause)
{
if (cause == NULL) {
return;
}
PyObject *exc = _PyErr_GetRaisedException(tstate);
assert(exc != NULL);
PyObject *ctx = PyException_GetContext(exc);
if (ctx == NULL) {
PyException_SetContext(exc, Py_NewRef(cause));
}
else {
Py_DECREF(ctx);
}
assert(PyException_GetCause(exc) == NULL);
PyException_SetCause(exc, Py_NewRef(cause));
_PyErr_SetRaisedException(tstate, exc);
}
/* InterpreterError extends Exception */
static PyTypeObject _PyExc_InterpreterError = {
@ -25,6 +46,97 @@ static PyTypeObject _PyExc_InterpreterNotFoundError = {
};
PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError;
/* NotShareableError extends ValueError */
static int
_init_notshareableerror(exceptions_t *state)
{
const char *name = "interpreters.NotShareableError";
// XXX Inherit from TypeError.
PyObject *base = PyExc_ValueError;
PyObject *ns = NULL;
PyObject *exctype = PyErr_NewException(name, base, ns);
if (exctype == NULL) {
return -1;
}
state->PyExc_NotShareableError = exctype;
return 0;
}
static void
_fini_notshareableerror(exceptions_t *state)
{
Py_CLEAR(state->PyExc_NotShareableError);
}
static PyObject *
get_notshareableerror_type(PyThreadState *tstate)
{
_PyXI_state_t *local = _PyXI_GET_STATE(tstate->interp);
if (local == NULL) {
PyErr_Clear();
return NULL;
}
return local->exceptions.PyExc_NotShareableError;
}
static void
_ensure_notshareableerror(PyThreadState *tstate,
PyObject *cause, int force, PyObject *msgobj)
{
PyObject *ctx = _PyErr_GetRaisedException(tstate);
PyObject *exctype = get_notshareableerror_type(tstate);
if (exctype != NULL) {
if (!force && ctx != NULL && Py_TYPE(ctx) == (PyTypeObject *)exctype) {
// A NotShareableError instance is already set.
assert(cause == NULL);
_PyErr_SetRaisedException(tstate, ctx);
}
}
else {
exctype = PyExc_ValueError;
}
_PyErr_SetObject(tstate, exctype, msgobj);
// We have to set the context manually since _PyErr_SetObject() doesn't.
_PyErr_ChainExceptions1Tstate(tstate, ctx);
_ensure_current_cause(tstate, cause);
}
static void
set_notshareableerror(PyThreadState *tstate, PyObject *cause, int force, const char *msg)
{
PyObject *msgobj = PyUnicode_FromString(msg);
if (msgobj == NULL) {
assert(_PyErr_Occurred(tstate));
}
else {
_ensure_notshareableerror(tstate, cause, force, msgobj);
}
}
static void
format_notshareableerror_v(PyThreadState *tstate, PyObject *cause, int force,
const char *format, va_list vargs)
{
PyObject *msgobj = PyUnicode_FromFormatV(format, vargs);
if (msgobj == NULL) {
assert(_PyErr_Occurred(tstate));
}
else {
_ensure_notshareableerror(tstate, cause, force, msgobj);
}
}
static void
format_notshareableerror(PyThreadState *tstate, PyObject *cause, int force,
const char *format, ...)
{
va_list vargs;
va_start(vargs, format);
format_notshareableerror_v(tstate, cause, force, format, vargs);
va_end(vargs);
}
/* lifecycle */
@ -76,18 +188,9 @@ fini_static_exctypes(exceptions_t *state, PyInterpreterState *interp)
static int
init_heap_exctypes(exceptions_t *state)
{
PyObject *exctype;
/* NotShareableError extends ValueError */
const char *name = "interpreters.NotShareableError";
PyObject *base = PyExc_ValueError;
PyObject *ns = NULL;
exctype = PyErr_NewException(name, base, ns);
if (exctype == NULL) {
if (_init_notshareableerror(state) < 0) {
goto error;
}
state->PyExc_NotShareableError = exctype;
return 0;
error:
@ -98,5 +201,5 @@ error:
static void
fini_heap_exctypes(exceptions_t *state)
{
Py_CLEAR(state->PyExc_NotShareableError);
_fini_notshareableerror(state);
}

View file

@ -21,11 +21,6 @@
#endif
/* Forward declarations */
static PyObject *
_PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs);
void
_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc)
{
@ -699,12 +694,11 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb)
The caller is responsible for ensuring that this call won't create
any cycles in the exception context chain. */
void
_PyErr_ChainExceptions1(PyObject *exc)
_PyErr_ChainExceptions1Tstate(PyThreadState *tstate, PyObject *exc)
{
if (exc == NULL) {
return;
}
PyThreadState *tstate = _PyThreadState_GET();
if (_PyErr_Occurred(tstate)) {
PyObject *exc2 = _PyErr_GetRaisedException(tstate);
PyException_SetContext(exc2, exc);
@ -715,6 +709,13 @@ _PyErr_ChainExceptions1(PyObject *exc)
}
}
void
_PyErr_ChainExceptions1(PyObject *exc)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyErr_ChainExceptions1Tstate(tstate, exc);
}
/* If the current thread is handling an exception (exc_info is ), set this
exception as the context of the current raised exception.
@ -1061,15 +1062,14 @@ PyObject *PyErr_SetFromWindowsErrWithFilename(
#endif /* MS_WINDOWS */
static PyObject *
_PyErr_SetImportErrorSubclassWithNameFrom(
PyObject *exception, PyObject *msg,
new_importerror(
PyThreadState *tstate, PyObject *exctype, PyObject *msg,
PyObject *name, PyObject *path, PyObject* from_name)
{
PyThreadState *tstate = _PyThreadState_GET();
int issubclass;
PyObject *kwargs, *error;
PyObject *exc = NULL;
PyObject *kwargs = NULL;
issubclass = PyObject_IsSubclass(exception, PyExc_ImportError);
int issubclass = PyObject_IsSubclass(exctype, PyExc_ImportError);
if (issubclass < 0) {
return NULL;
}
@ -1095,29 +1095,38 @@ _PyErr_SetImportErrorSubclassWithNameFrom(
from_name = Py_None;
}
kwargs = PyDict_New();
if (kwargs == NULL) {
return NULL;
}
if (PyDict_SetItemString(kwargs, "name", name) < 0) {
goto done;
goto finally;
}
if (PyDict_SetItemString(kwargs, "path", path) < 0) {
goto done;
goto finally;
}
if (PyDict_SetItemString(kwargs, "name_from", from_name) < 0) {
goto done;
goto finally;
}
exc = PyObject_VectorcallDict(exctype, &msg, 1, kwargs);
error = PyObject_VectorcallDict(exception, &msg, 1, kwargs);
finally:
Py_DECREF(kwargs);
return exc;
}
static PyObject *
_PyErr_SetImportErrorSubclassWithNameFrom(
PyObject *exception, PyObject *msg,
PyObject *name, PyObject *path, PyObject* from_name)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *error = new_importerror(
tstate, exception, msg, name, path, from_name);
if (error != NULL) {
_PyErr_SetObject(tstate, (PyObject *)Py_TYPE(error), error);
Py_DECREF(error);
}
done:
Py_DECREF(kwargs);
return NULL;
}
@ -1141,6 +1150,29 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path)
return PyErr_SetImportErrorSubclass(PyExc_ImportError, msg, name, path);
}
int
_PyErr_SetModuleNotFoundError(PyObject *name)
{
PyThreadState *tstate = _PyThreadState_GET();
if (name == NULL) {
_PyErr_SetString(tstate, PyExc_TypeError, "expected a name argument");
return -1;
}
PyObject *msg = PyUnicode_FromFormat("%S module not found", name);
if (msg == NULL) {
return -1;
}
PyObject *exctype = PyExc_ModuleNotFoundError;
PyObject *exc = new_importerror(tstate, exctype, msg, name, NULL, NULL);
Py_DECREF(msg);
if (exc == NULL) {
return -1;
}
_PyErr_SetObject(tstate, exctype, exc);
Py_DECREF(exc);
return 0;
}
void
_PyErr_BadInternalCall(const char *filename, int lineno)
{
@ -1164,7 +1196,7 @@ PyErr_BadInternalCall(void)
#define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__)
static PyObject *
PyObject *
_PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs)
{