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

Reply via email to