https://github.com/python/cpython/commit/cd9536a0872046cc7c151b61b457975e7718274a commit: cd9536a0872046cc7c151b61b457975e7718274a branch: main author: Eric Snow <ericsnowcurren...@gmail.com> committer: ericsnowcurrently <ericsnowcurren...@gmail.com> date: 2025-04-25T14:43:38-06:00 summary:
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(). files: M Include/internal/pycore_crossinterp.h M Include/internal/pycore_crossinterp_data_registry.h M Include/internal/pycore_pyerrors.h M Lib/test/test__interpreters.py M Lib/test/test_interpreters/test_api.py M Modules/_interpchannelsmodule.c M Modules/_interpqueuesmodule.c M Modules/_interpreters_common.h M Modules/_interpretersmodule.c M Modules/_testinternalcapi.c M Python/crossinterp.c M Python/crossinterp_data_lookup.h M Python/crossinterp_exceptions.h M Python/errors.c diff --git a/Include/internal/pycore_crossinterp.h b/Include/internal/pycore_crossinterp.h index d3226d2b425f66..c712bbba742eed 100644 --- a/Include/internal/pycore_crossinterp.h +++ b/Include/internal/pycore_crossinterp.h @@ -97,26 +97,22 @@ PyAPI_FUNC(void) _PyXIData_Free(_PyXIData_t *data); typedef int (*xidatafunc)(PyThreadState *tstate, PyObject *, _PyXIData_t *); -typedef struct _xid_lookup_state _PyXIData_lookup_t; - -typedef struct { - _PyXIData_lookup_t *global; - _PyXIData_lookup_t *local; - PyObject *PyExc_NotShareableError; -} _PyXIData_lookup_context_t; - -PyAPI_FUNC(int) _PyXIData_GetLookupContext( - PyInterpreterState *, - _PyXIData_lookup_context_t *); +PyAPI_FUNC(PyObject *) _PyXIData_GetNotShareableErrorType(PyThreadState *); +PyAPI_FUNC(void) _PyXIData_SetNotShareableError(PyThreadState *, const char *); +PyAPI_FUNC(void) _PyXIData_FormatNotShareableError( + PyThreadState *, + const char *, + ...); PyAPI_FUNC(xidatafunc) _PyXIData_Lookup( - _PyXIData_lookup_context_t *, + PyThreadState *, PyObject *); PyAPI_FUNC(int) _PyObject_CheckXIData( - _PyXIData_lookup_context_t *, + PyThreadState *, PyObject *); + PyAPI_FUNC(int) _PyObject_GetXIData( - _PyXIData_lookup_context_t *, + PyThreadState *, PyObject *, _PyXIData_t *); @@ -171,6 +167,8 @@ PyAPI_FUNC(void) _PyXIData_Clear( PyInterpreterState *, _PyXIData_t *); /* runtime state & lifecycle */ /*****************************/ +typedef struct _xid_lookup_state _PyXIData_lookup_t; + typedef struct { // builtin types _PyXIData_lookup_t data_lookup; diff --git a/Include/internal/pycore_crossinterp_data_registry.h b/Include/internal/pycore_crossinterp_data_registry.h index bbad4de770857f..8f4bcb948e5a45 100644 --- a/Include/internal/pycore_crossinterp_data_registry.h +++ b/Include/internal/pycore_crossinterp_data_registry.h @@ -28,11 +28,11 @@ typedef struct { } _PyXIData_registry_t; PyAPI_FUNC(int) _PyXIData_RegisterClass( - _PyXIData_lookup_context_t *, + PyThreadState *, PyTypeObject *, xidatafunc); PyAPI_FUNC(int) _PyXIData_UnregisterClass( - _PyXIData_lookup_context_t *, + PyThreadState *, PyTypeObject *); struct _xid_lookup_state { diff --git a/Include/internal/pycore_pyerrors.h b/Include/internal/pycore_pyerrors.h index fa7d9ee36d095d..f357b88e220e6e 100644 --- a/Include/internal/pycore_pyerrors.h +++ b/Include/internal/pycore_pyerrors.h @@ -60,6 +60,7 @@ extern PyObject* _PyErr_SetImportErrorWithNameFrom( PyObject *, PyObject *, PyObject *); +extern int _PyErr_SetModuleNotFoundError(PyObject *name); /* runtime lifecycle */ @@ -113,6 +114,7 @@ extern void _PyErr_SetObject( PyObject *value); extern void _PyErr_ChainStackItem(void); +extern void _PyErr_ChainExceptions1Tstate(PyThreadState *, PyObject *); PyAPI_FUNC(void) _PyErr_Clear(PyThreadState *tstate); @@ -148,6 +150,12 @@ PyAPI_FUNC(PyObject*) _PyErr_Format( const char *format, ...); +PyAPI_FUNC(PyObject*) _PyErr_FormatV( + PyThreadState *tstate, + PyObject *exception, + const char *format, + va_list vargs); + extern void _PyErr_NormalizeException( PyThreadState *tstate, PyObject **exc, diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index ec376773e40f62..aa46eb4f7b8653 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -15,7 +15,7 @@ _interpreters = import_helper.import_module('_interpreters') _testinternalcapi = import_helper.import_module('_testinternalcapi') -from _interpreters import InterpreterNotFoundError +from _interpreters import InterpreterNotFoundError, NotShareableError ################################## @@ -189,8 +189,9 @@ def test_non_shareable_int(self): ] for i in ints: with self.subTest(i): - with self.assertRaises(OverflowError): + with self.assertRaises(NotShareableError) as cm: _testinternalcapi.get_crossinterp_data(i) + self.assertIsInstance(cm.exception.__cause__, OverflowError) def test_bool(self): self._assert_values([True, False]) @@ -215,14 +216,12 @@ def test_tuples_containing_non_shareable_types(self): for s in non_shareables: value = tuple([0, 1.0, s]) with self.subTest(repr(value)): - # XXX Assert the NotShareableError when it is exported - with self.assertRaises(ValueError): + with self.assertRaises(NotShareableError): _testinternalcapi.get_crossinterp_data(value) # Check nested as well value = tuple([0, 1., (s,)]) with self.subTest("nested " + repr(value)): - # XXX Assert the NotShareableError when it is exported - with self.assertRaises(ValueError): + with self.assertRaises(NotShareableError): _testinternalcapi.get_crossinterp_data(value) diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index 01856d9bf67657..66c7afce88f0d8 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -693,8 +693,7 @@ def test_dict_and_kwargs(self): def test_not_shareable(self): interp = interpreters.create() - # XXX TypeError? - with self.assertRaises(ValueError): + with self.assertRaises(interpreters.NotShareableError): interp.prepare_main(spam={'spam': 'eggs', 'foo': 'bar'}) # Make sure neither was actually bound. diff --git a/Modules/_interpchannelsmodule.c b/Modules/_interpchannelsmodule.c index 6cc1a5b828627f..81306019f0f673 100644 --- a/Modules/_interpchannelsmodule.c +++ b/Modules/_interpchannelsmodule.c @@ -1754,17 +1754,10 @@ static int channel_send(_channels *channels, int64_t cid, PyObject *obj, _waiting_t *waiting, int unboundop) { - PyInterpreterState *interp = _get_current_interp(); - if (interp == NULL) { - return -1; - } + PyThreadState *tstate = _PyThreadState_GET(); + PyInterpreterState *interp = tstate->interp; int64_t interpid = PyInterpreterState_GetID(interp); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return -1; - } - // Look up the channel. PyThread_type_lock mutex = NULL; _channel_state *chan = NULL; @@ -1786,7 +1779,7 @@ channel_send(_channels *channels, int64_t cid, PyObject *obj, PyThread_release_lock(mutex); return -1; } - if (_PyObject_GetXIData(&ctx, obj, data) != 0) { + if (_PyObject_GetXIData(tstate, obj, data) != 0) { PyThread_release_lock(mutex); GLOBAL_FREE(data); return -1; diff --git a/Modules/_interpqueuesmodule.c b/Modules/_interpqueuesmodule.c index d069880e2750de..fdbf197e27950b 100644 --- a/Modules/_interpqueuesmodule.c +++ b/Modules/_interpqueuesmodule.c @@ -1127,11 +1127,7 @@ queue_destroy(_queues *queues, int64_t qid) static int queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) { - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return -1; - } + PyThreadState *tstate = PyThreadState_Get(); // Look up the queue. _queue *queue = NULL; @@ -1147,12 +1143,13 @@ queue_put(_queues *queues, int64_t qid, PyObject *obj, int fmt, int unboundop) _queue_unmark_waiter(queue, queues->mutex); return -1; } - if (_PyObject_GetXIData(&ctx, obj, data) != 0) { + if (_PyObject_GetXIData(tstate, obj, data) != 0) { _queue_unmark_waiter(queue, queues->mutex); GLOBAL_FREE(data); return -1; } - assert(_PyXIData_INTERPID(data) == PyInterpreterState_GetID(interp)); + assert(_PyXIData_INTERPID(data) == + PyInterpreterState_GetID(tstate->interp)); // Add the data to the queue. int64_t interpid = -1; // _queueitem_init() will set it. diff --git a/Modules/_interpreters_common.h b/Modules/_interpreters_common.h index a6c639feea5d14..edd65577284a20 100644 --- a/Modules/_interpreters_common.h +++ b/Modules/_interpreters_common.h @@ -8,24 +8,16 @@ static int ensure_xid_class(PyTypeObject *cls, xidatafunc getdata) { - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return -1; - } - return _PyXIData_RegisterClass(&ctx, cls, getdata); + PyThreadState *tstate = PyThreadState_Get(); + return _PyXIData_RegisterClass(tstate, cls, getdata); } #ifdef REGISTERS_HEAP_TYPES static int clear_xid_class(PyTypeObject *cls) { - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return -1; - } - return _PyXIData_UnregisterClass(&ctx, cls); + PyThreadState *tstate = PyThreadState_Get(); + return _PyXIData_UnregisterClass(tstate, cls); } #endif diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index e59a53feb09df8..8ced6873f08967 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1278,13 +1278,8 @@ object_is_shareable(PyObject *self, PyObject *args, PyObject *kwds) return NULL; } - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return NULL; - } - - if (_PyObject_CheckXIData(&ctx, obj) == 0) { + PyThreadState *tstate = _PyThreadState_GET(); + if (_PyObject_CheckXIData(tstate, obj) == 0) { Py_RETURN_TRUE; } PyErr_Clear(); @@ -1577,14 +1572,9 @@ The 'interpreters' module provides a more convenient interface."); static int module_exec(PyObject *mod) { - PyInterpreterState *interp = PyInterpreterState_Get(); + PyThreadState *tstate = _PyThreadState_GET(); module_state *state = get_module_state(mod); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return -1; - } - #define ADD_WHENCE(NAME) \ if (PyModule_AddIntConstant(mod, "WHENCE_" #NAME, \ _PyInterpreterState_WHENCE_##NAME) < 0) \ @@ -1606,7 +1596,8 @@ module_exec(PyObject *mod) if (PyModule_AddType(mod, (PyTypeObject *)PyExc_InterpreterNotFoundError) < 0) { goto error; } - if (PyModule_AddType(mod, (PyTypeObject *)ctx.PyExc_NotShareableError) < 0) { + PyObject *exctype = _PyXIData_GetNotShareableErrorType(tstate); + if (PyModule_AddType(mod, (PyTypeObject *)exctype) < 0) { goto error; } diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index f91470837edb1c..575e55a0e9c45a 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1696,11 +1696,7 @@ _xid_capsule_destructor(PyObject *capsule) static PyObject * get_crossinterp_data(PyObject *self, PyObject *args) { - PyInterpreterState *interp = PyInterpreterState_Get(); - _PyXIData_lookup_context_t ctx; - if (_PyXIData_GetLookupContext(interp, &ctx) < 0) { - return NULL; - } + PyThreadState *tstate = _PyThreadState_GET(); PyObject *obj = NULL; if (!PyArg_ParseTuple(args, "O:get_crossinterp_data", &obj)) { @@ -1711,7 +1707,7 @@ get_crossinterp_data(PyObject *self, PyObject *args) if (data == NULL) { return NULL; } - if (_PyObject_GetXIData(&ctx, obj, data) != 0) { + if (_PyObject_GetXIData(tstate, obj, data) != 0) { _PyXIData_Free(data); return NULL; } diff --git a/Python/crossinterp.c b/Python/crossinterp.c index 094bbbe54f2a75..e4368165a35326 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -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; diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 48e5d9762cd697..8358ba650739d1 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -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,28 +29,15 @@ xid_lookup_fini(_PyXIData_lookup_t *state) _xidregistry_fini(&state->registry); } -static xidatafunc -lookup_getdata(dlcontext_t *ctx, PyObject *obj) -{ - /* Cross-interpreter objects are looked up by exact match on the class. - We can reassess this policy when we move from a global registry to a - tp_* slot. */ - return _lookup_getdata_from_registry(ctx, obj); -} - - -/* exported API */ - -int -_PyXIData_GetLookupContext(PyInterpreterState *interp, - _PyXIData_lookup_context_t *res) +static int +get_lookup_context(PyThreadState *tstate, dlcontext_t *res) { - _PyXI_global_state_t *global = _PyXI_GET_GLOBAL_STATE(interp); + _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(interp); + _PyXI_state_t *local = _PyXI_GET_STATE(tstate->interp); if (local == NULL) { assert(PyErr_Occurred()); return -1; @@ -55,15 +45,57 @@ _PyXIData_GetLookupContext(PyInterpreterState *interp, *res = (dlcontext_t){ .global = &global->data_lookup, .local = &local->data_lookup, - .PyExc_NotShareableError = local->exceptions.PyExc_NotShareableError, }; return 0; } +static xidatafunc +lookup_getdata(dlcontext_t *ctx, PyObject *obj) +{ + /* Cross-interpreter objects are looked up by exact match on the class. + We can reassess this policy when we move from a global registry to a + tp_* slot. */ + return _lookup_getdata_from_registry(ctx, obj); +} + + +/* exported API */ + +PyObject * +_PyXIData_GetNotShareableErrorType(PyThreadState *tstate) +{ + PyObject *exctype = get_notshareableerror_type(tstate); + assert(exctype != NULL); + return exctype; +} + +void +_PyXIData_SetNotShareableError(PyThreadState *tstate, const char *msg) +{ + 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(_PyXIData_lookup_context_t *ctx, PyObject *obj) +_PyXIData_Lookup(PyThreadState *tstate, PyObject *obj) { - return lookup_getdata(ctx, 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 @@ _PyXIData_RegisterClass(_PyXIData_lookup_context_t *ctx, } 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) { diff --git a/Python/crossinterp_exceptions.h b/Python/crossinterp_exceptions.h index 3cb45d2067710b..39993648f275f4 100644 --- a/Python/crossinterp_exceptions.h +++ b/Python/crossinterp_exceptions.h @@ -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 @@ init_heap_exctypes(exceptions_t *state) static void fini_heap_exctypes(exceptions_t *state) { - Py_CLEAR(state->PyExc_NotShareableError); + _fini_notshareableerror(state); } diff --git a/Python/errors.c b/Python/errors.c index 2b088e2f3888a1..81f267b043afaf 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -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); + +finally: + Py_DECREF(kwargs); + return exc; +} - error = PyObject_VectorcallDict(exception, &msg, 1, kwargs); +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) { _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com