| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* API for managing interactions between isolated interpreters */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Python.h"
 | 
					
						
							| 
									
										
										
										
											2025-04-28 17:23:46 -06:00
										 |  |  | #include "marshal.h"              // PyMarshal_WriteObjectToString()
 | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | #include "osdefs.h"               // MAXPATHLEN
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | #include "pycore_ceval.h"         // _Py_simple_func
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | #include "pycore_crossinterp.h"   // _PyXIData_t
 | 
					
						
							| 
									
										
										
										
											2025-05-08 09:07:46 -06:00
										 |  |  | #include "pycore_function.h"      // _PyFunction_VerifyStateless()
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | #include "pycore_global_strings.h"  // _Py_ID()
 | 
					
						
							|  |  |  | #include "pycore_import.h"        // _PyImport_SetModule()
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | #include "pycore_initconfig.h"    // _PyStatus_OK()
 | 
					
						
							| 
									
										
										
										
											2025-03-20 12:35:23 +01:00
										 |  |  | #include "pycore_namespace.h"     // _PyNamespace_New()
 | 
					
						
							| 
									
										
										
										
											2025-05-08 09:07:46 -06:00
										 |  |  | #include "pycore_pythonrun.h"     // _Py_SourceAsString()
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | #include "pycore_runtime.h"       // _PyRuntime
 | 
					
						
							| 
									
										
										
										
											2025-05-12 16:10:56 -06:00
										 |  |  | #include "pycore_setobject.h"     // _PySet_NextEntry()
 | 
					
						
							| 
									
										
										
										
											2025-03-20 12:35:23 +01:00
										 |  |  | #include "pycore_typeobject.h"    // _PyStaticType_InitBuiltin()
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | static Py_ssize_t | 
					
						
							|  |  |  | _Py_GetMainfile(char *buffer, size_t maxlen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // We don't expect subinterpreters to have the __main__ module's
 | 
					
						
							|  |  |  |     // __name__ set, but proceed just in case.
 | 
					
						
							|  |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							|  |  |  |     PyObject *module = _Py_GetMainModule(tstate); | 
					
						
							|  |  |  |     if (_Py_CheckMainModule(module) < 0) { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |         Py_XDECREF(module); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_ssize_t size = _PyModule_GetFilenameUTF8(module, buffer, maxlen); | 
					
						
							|  |  |  |     Py_DECREF(module); | 
					
						
							|  |  |  |     return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | runpy_run_path(const char *filename, const char *modname) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *run_path = PyImport_ImportModuleAttrString("runpy", "run_path"); | 
					
						
							|  |  |  |     if (run_path == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *args = Py_BuildValue("(sOs)", filename, Py_None, modname); | 
					
						
							|  |  |  |     if (args == NULL) { | 
					
						
							|  |  |  |         Py_DECREF(run_path); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *ns = PyObject_Call(run_path, args, NULL); | 
					
						
							|  |  |  |     Py_DECREF(run_path); | 
					
						
							|  |  |  |     Py_DECREF(args); | 
					
						
							|  |  |  |     return ns; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | set_exc_with_cause(PyObject *exctype, const char *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *cause = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     PyErr_SetString(exctype, msg); | 
					
						
							|  |  |  |     PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     PyException_SetCause(exc, cause); | 
					
						
							|  |  |  |     PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | /****************************/ | 
					
						
							|  |  |  | /* module duplication utils */ | 
					
						
							|  |  |  | /****************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct sync_module_result { | 
					
						
							|  |  |  |     PyObject *module; | 
					
						
							|  |  |  |     PyObject *loaded; | 
					
						
							|  |  |  |     PyObject *failed; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct sync_module { | 
					
						
							|  |  |  |     const char *filename; | 
					
						
							|  |  |  |     char _filename[MAXPATHLEN+1]; | 
					
						
							|  |  |  |     struct sync_module_result cached; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | sync_module_clear(struct sync_module *data) | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     data->filename = NULL; | 
					
						
							|  |  |  |     Py_CLEAR(data->cached.module); | 
					
						
							|  |  |  |     Py_CLEAR(data->cached.loaded); | 
					
						
							|  |  |  |     Py_CLEAR(data->cached.failed); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | sync_module_capture_exc(PyThreadState *tstate, struct sync_module *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |     PyObject *context = data->cached.failed; | 
					
						
							|  |  |  |     PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     _PyErr_SetRaisedException(tstate, Py_NewRef(exc)); | 
					
						
							|  |  |  |     if (context != NULL) { | 
					
						
							|  |  |  |         PyException_SetContext(exc, context); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     data->cached.failed = exc; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | ensure_isolated_main(PyThreadState *tstate, struct sync_module *main) | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // Load the module from the original file (or from a cache).
 | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // First try the local cache.
 | 
					
						
							|  |  |  |     if (main->cached.failed != NULL) { | 
					
						
							|  |  |  |         // We'll deal with this in apply_isolated_main().
 | 
					
						
							|  |  |  |         assert(main->cached.module == NULL); | 
					
						
							|  |  |  |         assert(main->cached.loaded == NULL); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (main->cached.loaded != NULL) { | 
					
						
							|  |  |  |         assert(main->cached.module != NULL); | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(main->cached.module == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (main->filename == NULL) { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_NotImplementedError, ""); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // It wasn't in the local cache so we'll need to populate it.
 | 
					
						
							|  |  |  |     PyObject *mod = _Py_GetMainModule(tstate); | 
					
						
							|  |  |  |     if (_Py_CheckMainModule(mod) < 0) { | 
					
						
							|  |  |  |         // This is probably unrecoverable, so don't bother caching the error.
 | 
					
						
							|  |  |  |         assert(_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |         Py_XDECREF(mod); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     PyObject *loaded = NULL; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // Try the per-interpreter cache for the loaded module.
 | 
					
						
							|  |  |  |     // XXX Store it in sys.modules?
 | 
					
						
							|  |  |  |     PyObject *interpns = PyInterpreterState_GetDict(tstate->interp); | 
					
						
							|  |  |  |     assert(interpns != NULL); | 
					
						
							|  |  |  |     PyObject *key = PyUnicode_FromString("CACHED_MODULE_NS___main__"); | 
					
						
							|  |  |  |     if (key == NULL) { | 
					
						
							|  |  |  |         // It's probably unrecoverable, so don't bother caching the error.
 | 
					
						
							|  |  |  |         Py_DECREF(mod); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     else if (PyDict_GetItemRef(interpns, key, &loaded) < 0) { | 
					
						
							|  |  |  |         // It's probably unrecoverable, so don't bother caching the error.
 | 
					
						
							|  |  |  |         Py_DECREF(mod); | 
					
						
							|  |  |  |         Py_DECREF(key); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     else if (loaded == NULL) { | 
					
						
							|  |  |  |         // It wasn't already loaded from file.
 | 
					
						
							|  |  |  |         loaded = PyModule_NewObject(&_Py_ID(__main__)); | 
					
						
							|  |  |  |         if (loaded == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         PyObject *ns = _PyModule_GetDict(loaded); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |         // We don't want to trigger "if __name__ == '__main__':",
 | 
					
						
							|  |  |  |         // so we use a bogus module name.
 | 
					
						
							|  |  |  |         PyObject *loaded_ns = | 
					
						
							|  |  |  |                     runpy_run_path(main->filename, "<fake __main__>"); | 
					
						
							|  |  |  |         if (loaded_ns == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int res = PyDict_Update(ns, loaded_ns); | 
					
						
							|  |  |  |         Py_DECREF(loaded_ns); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |         // Set the per-interpreter cache entry.
 | 
					
						
							|  |  |  |         if (PyDict_SetItem(interpns, key, loaded) < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     Py_DECREF(key); | 
					
						
							|  |  |  |     main->cached = (struct sync_module_result){ | 
					
						
							|  |  |  |        .module = mod, | 
					
						
							|  |  |  |        .loaded = loaded, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     sync_module_capture_exc(tstate, main); | 
					
						
							|  |  |  |     Py_XDECREF(loaded); | 
					
						
							|  |  |  |     Py_DECREF(mod); | 
					
						
							|  |  |  |     Py_XDECREF(key); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | main_mod_matches(PyObject *expected) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *mod = PyImport_GetModule(&_Py_ID(__main__)); | 
					
						
							|  |  |  |     Py_XDECREF(mod); | 
					
						
							|  |  |  |     return mod == expected; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | apply_isolated_main(PyThreadState *tstate, struct sync_module *main) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert((main->cached.loaded == NULL) == (main->cached.loaded == NULL)); | 
					
						
							|  |  |  |     if (main->cached.failed != NULL) { | 
					
						
							|  |  |  |         // It must have failed previously.
 | 
					
						
							|  |  |  |         assert(main->cached.loaded == NULL); | 
					
						
							|  |  |  |         _PyErr_SetRaisedException(tstate, main->cached.failed); | 
					
						
							|  |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     assert(main->cached.loaded != NULL); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     assert(main_mod_matches(main->cached.module)); | 
					
						
							|  |  |  |     if (_PyImport_SetModule(&_Py_ID(__main__), main->cached.loaded) < 0) { | 
					
						
							|  |  |  |         sync_module_capture_exc(tstate, main); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | restore_main(PyThreadState *tstate, struct sync_module *main) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(main->cached.failed == NULL); | 
					
						
							|  |  |  |     assert(main->cached.module != NULL); | 
					
						
							|  |  |  |     assert(main->cached.loaded != NULL); | 
					
						
							|  |  |  |     PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     assert(main_mod_matches(main->cached.loaded)); | 
					
						
							|  |  |  |     int res = _PyImport_SetModule(&_Py_ID(__main__), main->cached.module); | 
					
						
							|  |  |  |     assert(res == 0); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         PyErr_FormatUnraisable("Exception ignored while restoring __main__"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyErr_SetRaisedException(tstate, exc); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | /**************/ | 
					
						
							|  |  |  | /* exceptions */ | 
					
						
							|  |  |  | /**************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-11 14:49:41 -07:00
										 |  |  | typedef struct xi_exceptions exceptions_t; | 
					
						
							|  |  |  | static int init_static_exctypes(exceptions_t *, PyInterpreterState *); | 
					
						
							|  |  |  | static void fini_static_exctypes(exceptions_t *, PyInterpreterState *); | 
					
						
							|  |  |  | static int init_heap_exctypes(exceptions_t *); | 
					
						
							|  |  |  | static void fini_heap_exctypes(exceptions_t *); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | #include "crossinterp_exceptions.h"
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | /***************************/ | 
					
						
							|  |  |  | /* cross-interpreter calls */ | 
					
						
							|  |  |  | /***************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _Py_CallInInterpreter(PyInterpreterState *interp, | 
					
						
							|  |  |  |                       _Py_simple_func func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     if (interp == PyInterpreterState_Get()) { | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return func(arg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // XXX Emit a warning if this fails?
 | 
					
						
							|  |  |  |     _PyEval_AddPendingCall(interp, (_Py_pending_call_func)func, arg, 0); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _Py_CallInInterpreterAndRawFree(PyInterpreterState *interp, | 
					
						
							|  |  |  |                                 _Py_simple_func func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     if (interp == PyInterpreterState_Get()) { | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         int res = func(arg); | 
					
						
							|  |  |  |         PyMem_RawFree(arg); | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // XXX Emit a warning if this fails?
 | 
					
						
							|  |  |  |     _PyEval_AddPendingCall(interp, func, arg, _Py_PENDING_RAWFREE); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**************************/ | 
					
						
							|  |  |  | /* cross-interpreter data */ | 
					
						
							|  |  |  | /**************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  | /* registry of {type -> _PyXIData_getdata_t} */ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  | /* For now we use a global registry of shareable classes.
 | 
					
						
							|  |  |  |    An alternative would be to add a tp_* slot for a class's | 
					
						
							|  |  |  |    _PyXIData_getdata_t.  It would be simpler and more efficient. */ | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | static void xid_lookup_init(_PyXIData_lookup_t *); | 
					
						
							|  |  |  | static void xid_lookup_fini(_PyXIData_lookup_t *); | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | struct _dlcontext; | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  | static _PyXIData_getdata_t lookup_getdata(struct _dlcontext *, PyObject *); | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | #include "crossinterp_data_lookup.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* lifecycle */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _PyXIData_t * | 
					
						
							|  |  |  | _PyXIData_New(void) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_t *xid = PyMem_RawCalloc(1, sizeof(_PyXIData_t)); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     if (xid == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return xid; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  | _PyXIData_Free(_PyXIData_t *xid) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyInterpreterState_Get(); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     _PyXIData_Clear(interp, xid); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     PyMem_RawFree(xid); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* defining cross-interpreter data */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _xidata_init(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     // If the value is being reused
 | 
					
						
							|  |  |  |     // then _xidata_clear() should have been called already.
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(xidata->data == NULL); | 
					
						
							|  |  |  |     assert(xidata->obj == NULL); | 
					
						
							|  |  |  |     *xidata = (_PyXIData_t){0}; | 
					
						
							|  |  |  |     _PyXIData_INTERPID(xidata) = -1; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _xidata_clear(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     // _PyXIData_t only has two members that need to be
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     // cleaned up, if set: "xidata" must be freed and "obj" must be decref'ed.
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     // In both cases the original (owning) interpreter must be used,
 | 
					
						
							|  |  |  |     // which is the caller's responsibility to ensure.
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if (xidata->data != NULL) { | 
					
						
							|  |  |  |         if (xidata->free != NULL) { | 
					
						
							|  |  |  |             xidata->free(xidata->data); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         xidata->data = NULL; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     Py_CLEAR(xidata->obj); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_Init(_PyXIData_t *xidata, | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |                PyInterpreterState *interp, | 
					
						
							|  |  |  |                void *shared, PyObject *obj, | 
					
						
							| 
									
										
										
										
											2024-11-11 15:58:46 -07:00
										 |  |  |                xid_newobjfunc new_object) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(xidata != NULL); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     assert(new_object != NULL); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _xidata_init(xidata); | 
					
						
							|  |  |  |     xidata->data = shared; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     if (obj != NULL) { | 
					
						
							|  |  |  |         assert(interp != NULL); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |         // released in _PyXIData_Clear()
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         xidata->obj = Py_NewRef(obj); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     // Ideally every object would know its owning interpreter.
 | 
					
						
							|  |  |  |     // Until then, we have to rely on the caller to identify it
 | 
					
						
							|  |  |  |     // (but we don't need it in all cases).
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_INTERPID(xidata) = (interp != NULL) | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |         ? PyInterpreterState_GetID(interp) | 
					
						
							|  |  |  |         : -1; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     xidata->new_object = new_object; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_InitWithSize(_PyXIData_t *xidata, | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |                        PyInterpreterState *interp, | 
					
						
							|  |  |  |                        const size_t size, PyObject *obj, | 
					
						
							| 
									
										
										
										
											2024-11-11 15:58:46 -07:00
										 |  |  |                        xid_newobjfunc new_object) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(size > 0); | 
					
						
							|  |  |  |     // For now we always free the shared data in the same interpreter
 | 
					
						
							|  |  |  |     // where it was allocated, so the interpreter is required.
 | 
					
						
							|  |  |  |     assert(interp != NULL); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_Init(xidata, interp, NULL, obj, new_object); | 
					
						
							| 
									
										
										
										
											2025-04-28 12:52:36 -06:00
										 |  |  |     xidata->data = PyMem_RawCalloc(1, size); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if (xidata->data == NULL) { | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     xidata->free = PyMem_RawFree; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_Clear(PyInterpreterState *interp, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(xidata != NULL); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     // This must be called in the owning interpreter.
 | 
					
						
							|  |  |  |     assert(interp == NULL | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |            || _PyXIData_INTERPID(xidata) == -1 | 
					
						
							|  |  |  |            || _PyXIData_INTERPID(xidata) == PyInterpreterState_GetID(interp)); | 
					
						
							|  |  |  |     _xidata_clear(xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | /* getting cross-interpreter data */ | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static inline void | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | _set_xid_lookup_failure(PyThreadState *tstate, PyObject *obj, const char *msg, | 
					
						
							|  |  |  |                         PyObject *cause) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (msg != NULL) { | 
					
						
							|  |  |  |         assert(obj == NULL); | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         set_notshareableerror(tstate, cause, 0, msg); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (obj == NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         msg = "object does not support cross-interpreter data"; | 
					
						
							|  |  |  |         set_notshareableerror(tstate, cause, 0, msg); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |         msg = "%R does not support cross-interpreter data"; | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         format_notshareableerror(tstate, cause, 0, msg, obj); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  | _PyObject_CheckXIData(PyThreadState *tstate, PyObject *obj) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     dlcontext_t ctx; | 
					
						
							|  |  |  |     if (get_lookup_context(tstate, &ctx) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |     _PyXIData_getdata_t getdata = lookup_getdata(&ctx, obj); | 
					
						
							|  |  |  |     if (getdata.basic == NULL && getdata.fallback == NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         if (!_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |             _set_xid_lookup_failure(tstate, obj, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _check_xidata(PyThreadState *tstate, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // xidata->data can be anything, including NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // xidata->obj may be NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (_PyXIData_INTERPID(xidata) < 0) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_SystemError, "missing interp"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (xidata->new_object == NULL) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_SystemError, "missing new_object func"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // xidata->free may be NULL, so we don't check it.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _get_xidata(PyThreadState *tstate, | 
					
						
							|  |  |  |             PyObject *obj, xidata_fallback_t fallback, _PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     PyInterpreterState *interp = tstate->interp; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(xidata->data == NULL); | 
					
						
							|  |  |  |     assert(xidata->obj == NULL); | 
					
						
							|  |  |  |     if (xidata->data != NULL || xidata->obj != NULL) { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_ValueError, "xidata not cleared"); | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Call the "getdata" func for the object.
 | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     dlcontext_t ctx; | 
					
						
							|  |  |  |     if (get_lookup_context(tstate, &ctx) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     Py_INCREF(obj); | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |     _PyXIData_getdata_t getdata = lookup_getdata(&ctx, obj); | 
					
						
							|  |  |  |     if (getdata.basic == NULL && getdata.fallback == NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         if (PyErr_Occurred()) { | 
					
						
							|  |  |  |             Py_DECREF(obj); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Fall back to obj
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         Py_DECREF(obj); | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         if (!_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |             _set_xid_lookup_failure(tstate, obj, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |     int res = getdata.basic != NULL | 
					
						
							|  |  |  |         ? getdata.basic(tstate, obj, xidata) | 
					
						
							|  |  |  |         : getdata.fallback(tstate, obj, fallback, xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     Py_DECREF(obj); | 
					
						
							|  |  |  |     if (res != 0) { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         _set_xid_lookup_failure(tstate, obj, NULL, cause); | 
					
						
							|  |  |  |         Py_XDECREF(cause); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fill in the blanks and validate the result.
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_INTERPID(xidata) = PyInterpreterState_GetID(interp); | 
					
						
							|  |  |  |     if (_check_xidata(tstate, xidata) != 0) { | 
					
						
							|  |  |  |         (void)_PyXIData_Release(xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  | int | 
					
						
							|  |  |  | _PyObject_GetXIDataNoFallback(PyThreadState *tstate, | 
					
						
							|  |  |  |                               PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _get_xidata(tstate, obj, _PyXIDATA_XIDATA_ONLY, xidata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyObject_GetXIData(PyThreadState *tstate, | 
					
						
							|  |  |  |                     PyObject *obj, xidata_fallback_t fallback, | 
					
						
							|  |  |  |                     _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     switch (fallback) { | 
					
						
							|  |  |  |         case _PyXIDATA_XIDATA_ONLY: | 
					
						
							|  |  |  |             return _get_xidata(tstate, obj, fallback, xidata); | 
					
						
							|  |  |  |         case _PyXIDATA_FULL_FALLBACK: | 
					
						
							|  |  |  |             if (_get_xidata(tstate, obj, fallback, xidata) == 0) { | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |             if (PyFunction_Check(obj)) { | 
					
						
							|  |  |  |                 if (_PyFunction_GetXIData(tstate, obj, xidata) == 0) { | 
					
						
							|  |  |  |                     Py_DECREF(exc); | 
					
						
							|  |  |  |                     return 0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 _PyErr_Clear(tstate); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // We could try _PyMarshal_GetXIData() but we won't for now.
 | 
					
						
							|  |  |  |             if (_PyPickle_GetXIData(tstate, obj, xidata) == 0) { | 
					
						
							|  |  |  |                 Py_DECREF(exc); | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Raise the original exception.
 | 
					
						
							|  |  |  |             _PyErr_SetRaisedException(tstate, exc); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |             Py_FatalError("unsupported xidata fallback option"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |             _PyErr_SetString(tstate, PyExc_SystemError, | 
					
						
							|  |  |  |                              "unsupported xidata fallback option"); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | /* pickle C-API */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _pickle_context { | 
					
						
							|  |  |  |     PyThreadState *tstate; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _PyPickle_Dumps(struct _pickle_context *ctx, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *dumps = PyImport_ImportModuleAttrString("pickle", "dumps"); | 
					
						
							|  |  |  |     if (dumps == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *bytes = PyObject_CallOneArg(dumps, obj); | 
					
						
							|  |  |  |     Py_DECREF(dumps); | 
					
						
							|  |  |  |     return bytes; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _unpickle_context { | 
					
						
							|  |  |  |     PyThreadState *tstate; | 
					
						
							|  |  |  |     // We only special-case the __main__ module,
 | 
					
						
							|  |  |  |     // since other modules behave consistently.
 | 
					
						
							|  |  |  |     struct sync_module main; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _unpickle_context_clear(struct _unpickle_context *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     sync_module_clear(&ctx->main); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | check_missing___main___attr(PyObject *exc) | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |     if (!PyErr_GivenExceptionMatches(exc, PyExc_AttributeError)) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Get the error message.
 | 
					
						
							|  |  |  |     PyObject *args = PyException_GetArgs(exc); | 
					
						
							|  |  |  |     if (args == NULL || args == Py_None || PyObject_Size(args) < 1) { | 
					
						
							|  |  |  |         assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     PyObject *msgobj = args; | 
					
						
							|  |  |  |     if (!PyUnicode_Check(msgobj)) { | 
					
						
							|  |  |  |         msgobj = PySequence_GetItem(args, 0); | 
					
						
							|  |  |  |         Py_DECREF(args); | 
					
						
							|  |  |  |         if (msgobj == NULL) { | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const char *err = PyUnicode_AsUTF8(msgobj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Check if it's a missing __main__ attr.
 | 
					
						
							|  |  |  |     int cmp = strncmp(err, "module '__main__' has no attribute '", 36); | 
					
						
							|  |  |  |     Py_DECREF(msgobj); | 
					
						
							|  |  |  |     return cmp == 0; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _PyPickle_Loads(struct _unpickle_context *ctx, PyObject *pickled) | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     PyThreadState *tstate = ctx->tstate; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     PyObject *exc = NULL; | 
					
						
							|  |  |  |     PyObject *loads = PyImport_ImportModuleAttrString("pickle", "loads"); | 
					
						
							|  |  |  |     if (loads == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // Make an initial attempt to unpickle.
 | 
					
						
							|  |  |  |     PyObject *obj = PyObject_CallOneArg(loads, pickled); | 
					
						
							|  |  |  |     if (obj != NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |         goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     assert(_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |     if (ctx == NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |         goto finally; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     if (!check_missing___main___attr(exc)) { | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // Temporarily swap in a fake __main__ loaded from the original
 | 
					
						
							|  |  |  |     // file and cached.  Note that functions will use the cached ns
 | 
					
						
							|  |  |  |     // for __globals__, // not the actual module.
 | 
					
						
							|  |  |  |     if (ensure_isolated_main(tstate, &ctx->main) < 0) { | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     if (apply_isolated_main(tstate, &ctx->main) < 0) { | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     // Try to unpickle once more.
 | 
					
						
							|  |  |  |     obj = PyObject_CallOneArg(loads, pickled); | 
					
						
							|  |  |  |     restore_main(tstate, &ctx->main); | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         goto finally; | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |     Py_CLEAR(exc); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  | finally: | 
					
						
							|  |  |  |     if (exc != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-18 17:57:14 -06:00
										 |  |  |         if (_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |             sync_module_capture_exc(tstate, &ctx->main); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |         // We restore the original exception.
 | 
					
						
							|  |  |  |         // It might make sense to chain it (__context__).
 | 
					
						
							|  |  |  |         _PyErr_SetRaisedException(tstate, exc); | 
					
						
							| 
									
										
										
										
											2025-04-30 17:34:05 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     Py_DECREF(loads); | 
					
						
							|  |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* pickle wrapper */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _pickle_xid_context { | 
					
						
							|  |  |  |     // __main__.__file__
 | 
					
						
							|  |  |  |     struct { | 
					
						
							|  |  |  |         const char *utf8; | 
					
						
							|  |  |  |         size_t len; | 
					
						
							|  |  |  |         char _utf8[MAXPATHLEN+1]; | 
					
						
							|  |  |  |     } mainfile; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _set_pickle_xid_context(PyThreadState *tstate, struct _pickle_xid_context *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Set mainfile if possible.
 | 
					
						
							|  |  |  |     Py_ssize_t len = _Py_GetMainfile(ctx->mainfile._utf8, MAXPATHLEN); | 
					
						
							|  |  |  |     if (len < 0) { | 
					
						
							|  |  |  |         // For now we ignore any exceptions.
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (len > 0) { | 
					
						
							|  |  |  |         ctx->mainfile.utf8 = ctx->mainfile._utf8; | 
					
						
							|  |  |  |         ctx->mainfile.len = (size_t)len; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _shared_pickle_data { | 
					
						
							|  |  |  |     _PyBytes_data_t pickled;  // Must be first if we use _PyBytes_FromXIData().
 | 
					
						
							|  |  |  |     struct _pickle_xid_context ctx; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyPickle_LoadFromXIData(_PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							|  |  |  |     struct _shared_pickle_data *shared = | 
					
						
							|  |  |  |                             (struct _shared_pickle_data *)xidata->data; | 
					
						
							|  |  |  |     // We avoid copying the pickled data by wrapping it in a memoryview.
 | 
					
						
							|  |  |  |     // The alternative is to get a bytes object using _PyBytes_FromXIData().
 | 
					
						
							|  |  |  |     PyObject *pickled = PyMemoryView_FromMemory( | 
					
						
							|  |  |  |             (char *)shared->pickled.bytes, shared->pickled.len, PyBUF_READ); | 
					
						
							|  |  |  |     if (pickled == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Unpickle the object.
 | 
					
						
							|  |  |  |     struct _unpickle_context ctx = { | 
					
						
							|  |  |  |         .tstate = tstate, | 
					
						
							|  |  |  |         .main = { | 
					
						
							|  |  |  |             .filename = shared->ctx.mainfile.utf8, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     PyObject *obj = _PyPickle_Loads(&ctx, pickled); | 
					
						
							|  |  |  |     Py_DECREF(pickled); | 
					
						
							|  |  |  |     _unpickle_context_clear(&ctx); | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         _set_xid_lookup_failure( | 
					
						
							|  |  |  |                     tstate, NULL, "object could not be unpickled", cause); | 
					
						
							|  |  |  |         Py_DECREF(cause); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyPickle_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Pickle the object.
 | 
					
						
							|  |  |  |     struct _pickle_context ctx = { | 
					
						
							|  |  |  |         .tstate = tstate, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     PyObject *bytes = _PyPickle_Dumps(&ctx, obj); | 
					
						
							|  |  |  |     if (bytes == NULL) { | 
					
						
							|  |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         _set_xid_lookup_failure( | 
					
						
							|  |  |  |                     tstate, NULL, "object could not be pickled", cause); | 
					
						
							|  |  |  |         Py_DECREF(cause); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If we had an "unwrapper" mechnanism, we could call
 | 
					
						
							|  |  |  |     // _PyObject_GetXIData() on the bytes object directly and add
 | 
					
						
							|  |  |  |     // a simple unwrapper to call pickle.loads() on the bytes.
 | 
					
						
							|  |  |  |     size_t size = sizeof(struct _shared_pickle_data); | 
					
						
							|  |  |  |     struct _shared_pickle_data *shared = | 
					
						
							|  |  |  |             (struct _shared_pickle_data *)_PyBytes_GetXIDataWrapped( | 
					
						
							|  |  |  |                     tstate, bytes, size, _PyPickle_LoadFromXIData, xidata); | 
					
						
							|  |  |  |     Py_DECREF(bytes); | 
					
						
							|  |  |  |     if (shared == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If it mattered, we could skip getting __main__.__file__
 | 
					
						
							|  |  |  |     // when "__main__" doesn't show up in the pickle bytes.
 | 
					
						
							|  |  |  |     if (_set_pickle_xid_context(tstate, &shared->ctx) < 0) { | 
					
						
							|  |  |  |         _xidata_clear(xidata); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 17:23:46 -06:00
										 |  |  | /* marshal wrapper */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyMarshal_ReadObjectFromXIData(_PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							|  |  |  |     _PyBytes_data_t *shared = (_PyBytes_data_t *)xidata->data; | 
					
						
							|  |  |  |     PyObject *obj = PyMarshal_ReadObjectFromString(shared->bytes, shared->len); | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         _set_xid_lookup_failure( | 
					
						
							|  |  |  |                     tstate, NULL, "object could not be unmarshalled", cause); | 
					
						
							|  |  |  |         Py_DECREF(cause); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return obj; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyMarshal_GetXIData(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *bytes = PyMarshal_WriteObjectToString(obj, Py_MARSHAL_VERSION); | 
					
						
							|  |  |  |     if (bytes == NULL) { | 
					
						
							|  |  |  |         PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         assert(cause != NULL); | 
					
						
							|  |  |  |         _set_xid_lookup_failure( | 
					
						
							|  |  |  |                     tstate, NULL, "object could not be marshalled", cause); | 
					
						
							|  |  |  |         Py_DECREF(cause); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     size_t size = sizeof(_PyBytes_data_t); | 
					
						
							|  |  |  |     _PyBytes_data_t *shared = _PyBytes_GetXIDataWrapped( | 
					
						
							|  |  |  |             tstate, bytes, size, _PyMarshal_ReadObjectFromXIData, xidata); | 
					
						
							|  |  |  |     Py_DECREF(bytes); | 
					
						
							|  |  |  |     if (shared == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-08 09:07:46 -06:00
										 |  |  | /* script wrapper */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | verify_script(PyThreadState *tstate, PyCodeObject *co, int checked, int pure) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Make sure it isn't a closure and (optionally) doesn't use globals.
 | 
					
						
							|  |  |  |     PyObject *builtins = NULL; | 
					
						
							|  |  |  |     if (pure) { | 
					
						
							|  |  |  |         builtins = _PyEval_GetBuiltins(tstate); | 
					
						
							|  |  |  |         assert(builtins != NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (checked) { | 
					
						
							|  |  |  |         assert(_PyCode_VerifyStateless(tstate, co, NULL, NULL, builtins) == 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (_PyCode_VerifyStateless(tstate, co, NULL, NULL, builtins) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Make sure it doesn't have args.
 | 
					
						
							|  |  |  |     if (co->co_argcount > 0 | 
					
						
							|  |  |  |         || co->co_posonlyargcount > 0 | 
					
						
							|  |  |  |         || co->co_kwonlyargcount > 0 | 
					
						
							|  |  |  |         || co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_ValueError, | 
					
						
							|  |  |  |                          "code with args not supported"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // Make sure it doesn't return anything.
 | 
					
						
							|  |  |  |     if (!_PyCode_ReturnsOnlyNone(co)) { | 
					
						
							|  |  |  |         _PyErr_SetString(tstate, PyExc_ValueError, | 
					
						
							|  |  |  |                          "code that returns a value is not a script"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | get_script_xidata(PyThreadState *tstate, PyObject *obj, int pure, | 
					
						
							|  |  |  |                   _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Get the corresponding code object.
 | 
					
						
							|  |  |  |     PyObject *code = NULL; | 
					
						
							|  |  |  |     int checked = 0; | 
					
						
							|  |  |  |     if (PyCode_Check(obj)) { | 
					
						
							|  |  |  |         code = obj; | 
					
						
							|  |  |  |         Py_INCREF(code); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (PyFunction_Check(obj)) { | 
					
						
							|  |  |  |         code = PyFunction_GET_CODE(obj); | 
					
						
							|  |  |  |         assert(code != NULL); | 
					
						
							|  |  |  |         Py_INCREF(code); | 
					
						
							|  |  |  |         if (pure) { | 
					
						
							|  |  |  |             if (_PyFunction_VerifyStateless(tstate, obj) < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             checked = 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         const char *filename = "<script>"; | 
					
						
							|  |  |  |         int optimize = 0; | 
					
						
							|  |  |  |         PyCompilerFlags cf = _PyCompilerFlags_INIT; | 
					
						
							|  |  |  |         cf.cf_flags = PyCF_SOURCE_IS_UTF8; | 
					
						
							|  |  |  |         PyObject *ref = NULL; | 
					
						
							|  |  |  |         const char *script = _Py_SourceAsString(obj, "???", "???", &cf, &ref); | 
					
						
							|  |  |  |         if (script == NULL) { | 
					
						
							|  |  |  |             if (!_PyObject_SupportedAsScript(obj)) { | 
					
						
							|  |  |  |                 // We discard the raised exception.
 | 
					
						
							|  |  |  |                 _PyErr_Format(tstate, PyExc_TypeError, | 
					
						
							|  |  |  |                               "unsupported script %R", obj); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-27 02:46:49 +09:00
										 |  |  | #ifdef Py_GIL_DISABLED
 | 
					
						
							|  |  |  |         // Don't immortalize code constants to avoid memory leaks.
 | 
					
						
							|  |  |  |         ((_PyThreadStateImpl *)tstate)->suppress_co_const_immortalization++; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-05-08 09:07:46 -06:00
										 |  |  |         code = Py_CompileStringExFlags( | 
					
						
							|  |  |  |                     script, filename, Py_file_input, &cf, optimize); | 
					
						
							| 
									
										
										
										
											2025-05-27 02:46:49 +09:00
										 |  |  | #ifdef Py_GIL_DISABLED
 | 
					
						
							|  |  |  |         ((_PyThreadStateImpl *)tstate)->suppress_co_const_immortalization--; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-05-08 09:07:46 -06:00
										 |  |  |         Py_XDECREF(ref); | 
					
						
							|  |  |  |         if (code == NULL) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // Compiled text can't have args or any return statements,
 | 
					
						
							|  |  |  |         // nor be a closure.  It can use globals though.
 | 
					
						
							|  |  |  |         if (!pure) { | 
					
						
							|  |  |  |             // We don't need to check for globals either.
 | 
					
						
							|  |  |  |             checked = 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Make sure it's actually a script.
 | 
					
						
							|  |  |  |     if (verify_script(tstate, (PyCodeObject *)code, checked, pure) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Convert the code object.
 | 
					
						
							|  |  |  |     int res = _PyCode_GetXIData(tstate, code, xidata); | 
					
						
							|  |  |  |     Py_DECREF(code); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_XDECREF(code); | 
					
						
							|  |  |  |     PyObject *cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     assert(cause != NULL); | 
					
						
							|  |  |  |     _set_xid_lookup_failure( | 
					
						
							|  |  |  |                 tstate, NULL, "object not a valid script", cause); | 
					
						
							|  |  |  |     Py_DECREF(cause); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCode_GetScriptXIData(PyThreadState *tstate, | 
					
						
							|  |  |  |                         PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return get_script_xidata(tstate, obj, 0, xidata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyCode_GetPureScriptXIData(PyThreadState *tstate, | 
					
						
							|  |  |  |                             PyObject *obj, _PyXIData_t *xidata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return get_script_xidata(tstate, obj, 1, xidata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | /* using cross-interpreter data */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | PyObject * | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_NewObject(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     return xidata->new_object(xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _call_clear_xidata(void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     _xidata_clear((_PyXIData_t *)data); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _xidata_release(_PyXIData_t *xidata, int rawfree) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if ((xidata->data == NULL || xidata->free == NULL) && xidata->obj == NULL) { | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         // Nothing to release!
 | 
					
						
							|  |  |  |         if (rawfree) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             PyMem_RawFree(xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             xidata->data = NULL; | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to the original interpreter.
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterState_LookUpID( | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |                                         _PyXIData_INTERPID(xidata)); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     if (interp == NULL) { | 
					
						
							|  |  |  |         // The interpreter was already destroyed.
 | 
					
						
							|  |  |  |         // This function shouldn't have been called.
 | 
					
						
							|  |  |  |         // XXX Someone leaked some memory...
 | 
					
						
							|  |  |  |         assert(PyErr_Occurred()); | 
					
						
							|  |  |  |         if (rawfree) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |             PyMem_RawFree(xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // "Release" the data and/or the object.
 | 
					
						
							|  |  |  |     if (rawfree) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         return _Py_CallInInterpreterAndRawFree(interp, _call_clear_xidata, xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         return _Py_CallInInterpreter(interp, _call_clear_xidata, xidata); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_Release(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     return _xidata_release(xidata, 0); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _PyXIData_ReleaseAndRawFree(_PyXIData_t *xidata) | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     return _xidata_release(xidata, 1); | 
					
						
							| 
									
										
										
										
											2023-10-30 16:53:10 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /*************************/ | 
					
						
							|  |  |  | /* convenience utilities */ | 
					
						
							|  |  |  | /*************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | static const char * | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | _copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     Py_ssize_t size = -1; | 
					
						
							|  |  |  |     const char *str = PyUnicode_AsUTF8AndSize(strobj, &size); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (str == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-01 01:33:23 -05:00
										 |  |  |     if (size != (Py_ssize_t)strlen(str)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "found embedded NULL character"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     char *copied = PyMem_RawMalloc(size+1); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (copied == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     strcpy(copied, str); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (p_size != NULL) { | 
					
						
							|  |  |  |         *p_size = size; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return copied; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | _convert_exc_to_TracebackException(PyObject *exc, PyObject **p_tbexc) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *args = NULL; | 
					
						
							|  |  |  |     PyObject *kwargs = NULL; | 
					
						
							|  |  |  |     PyObject *create = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // This is inspired by _PyErr_Display().
 | 
					
						
							| 
									
										
										
										
											2025-02-05 11:03:58 +01:00
										 |  |  |     PyObject *tbexc_type = PyImport_ImportModuleAttrString( | 
					
						
							|  |  |  |         "traceback", | 
					
						
							|  |  |  |         "TracebackException"); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (tbexc_type == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     create = PyObject_GetAttrString(tbexc_type, "from_exception"); | 
					
						
							|  |  |  |     Py_DECREF(tbexc_type); | 
					
						
							|  |  |  |     if (create == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     args = PyTuple_Pack(1, exc); | 
					
						
							|  |  |  |     if (args == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     kwargs = PyDict_New(); | 
					
						
							|  |  |  |     if (kwargs == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(kwargs, "save_exc_type", Py_False) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(kwargs, "lookup_lines", Py_False) < 0) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *tbexc = PyObject_Call(create, args, kwargs); | 
					
						
							|  |  |  |     Py_DECREF(args); | 
					
						
							|  |  |  |     Py_DECREF(kwargs); | 
					
						
							|  |  |  |     Py_DECREF(create); | 
					
						
							|  |  |  |     if (tbexc == NULL) { | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     *p_tbexc = tbexc; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_XDECREF(args); | 
					
						
							|  |  |  |     Py_XDECREF(kwargs); | 
					
						
							|  |  |  |     Py_XDECREF(create); | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  | // We accommodate backports here.
 | 
					
						
							|  |  |  | #ifndef _Py_EMPTY_STR
 | 
					
						
							|  |  |  | # define _Py_EMPTY_STR &_Py_STR(empty)
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							|  |  |  | _format_TracebackException(PyObject *tbexc) | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     PyObject *lines = PyObject_CallMethod(tbexc, "format", NULL); | 
					
						
							|  |  |  |     if (lines == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-02-13 14:56:49 -07:00
										 |  |  |     assert(_Py_EMPTY_STR != NULL); | 
					
						
							|  |  |  |     PyObject *formatted_obj = PyUnicode_Join(_Py_EMPTY_STR, lines); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     Py_DECREF(lines); | 
					
						
							|  |  |  |     if (formatted_obj == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Py_ssize_t size = -1; | 
					
						
							|  |  |  |     const char *formatted = _copy_string_obj_raw(formatted_obj, &size); | 
					
						
							|  |  |  |     Py_DECREF(formatted_obj); | 
					
						
							|  |  |  |     // We remove trailing the newline added by TracebackException.format().
 | 
					
						
							|  |  |  |     assert(formatted[size-1] == '\n'); | 
					
						
							|  |  |  |     ((char *)formatted)[size-1] = '\0'; | 
					
						
							|  |  |  |     return formatted; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  | _release_xid_data(_PyXIData_t *xidata, int rawfree) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |     int res = rawfree | 
					
						
							| 
									
										
										
										
											2025-10-19 22:24:28 +03:00
										 |  |  |         ? _PyXIData_ReleaseAndRawFree(xidata) | 
					
						
							|  |  |  |         : _PyXIData_Release(xidata); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         /* The owning interpreter is already destroyed. */ | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         _PyXIData_Clear(NULL, xidata); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // XXX Emit a warning?
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | /***********************/ | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | /* exception snapshots */ | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | /***********************/ | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | _excinfo_init_type_from_exception(struct _excinfo_type *info, PyObject *exc) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     /* 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; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     else { | 
					
						
							|  |  |  |         // Only builtin types are preserved.
 | 
					
						
							|  |  |  |         info->builtin = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // __name__
 | 
					
						
							|  |  |  |     strobj = PyType_GetName(type); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->name = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     // __qualname__
 | 
					
						
							|  |  |  |     strobj = PyType_GetQualName(type); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->qualname = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     if (info->qualname == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // __module__
 | 
					
						
							| 
									
										
										
										
											2024-03-14 19:17:43 +01:00
										 |  |  |     strobj = PyType_GetModuleName(type); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (strobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->module = _copy_string_obj_raw(strobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(strobj); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     if (info->module == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _excinfo_init_type_from_object(struct _excinfo_type *info, PyObject *exctype) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *strobj = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // __name__
 | 
					
						
							|  |  |  |     strobj = PyObject_GetAttrString(exctype, "__name__"); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     info->name = _copy_string_obj_raw(strobj, NULL); | 
					
						
							|  |  |  |     Py_DECREF(strobj); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (info->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     // __qualname__
 | 
					
						
							|  |  |  |     strobj = PyObject_GetAttrString(exctype, "__qualname__"); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     info->qualname = _copy_string_obj_raw(strobj, NULL); | 
					
						
							|  |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->qualname == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // __module__
 | 
					
						
							|  |  |  |     strobj = PyObject_GetAttrString(exctype, "__module__"); | 
					
						
							|  |  |  |     if (strobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     info->module = _copy_string_obj_raw(strobj, NULL); | 
					
						
							|  |  |  |     Py_DECREF(strobj); | 
					
						
							|  |  |  |     if (info->module == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _excinfo_clear_type(struct _excinfo_type *info) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     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; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     assert(module != NULL); | 
					
						
							|  |  |  |     if (strcmp(module, "builtins") == 0) { | 
					
						
							|  |  |  |         module = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (strcmp(module, "__main__") == 0) { | 
					
						
							|  |  |  |         module = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *p_qualname = qualname; | 
					
						
							|  |  |  |     *p_module = module; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | excinfo_is_set(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return info->type.name != NULL || info->msg != NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | _PyXI_excinfo_clear(_PyXI_excinfo *info) | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     _excinfo_clear_type(&info->type); | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     if (info->msg != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->msg); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)info->errdisplay); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     *info = (_PyXI_excinfo){{NULL}}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | PyObject * | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _PyXI_excinfo_InitFromException(_PyXI_excinfo *info, PyObject *exc) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(exc != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     if (PyErr_GivenExceptionMatches(exc, PyExc_MemoryError)) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         _PyXI_excinfo_clear(info); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     if (_excinfo_init_type_from_exception(&info->type, exc) < 0) { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         failure = "error while initializing exception type snapshot"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Extract the exception message.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     PyObject *msgobj = PyObject_Str(exc); | 
					
						
							|  |  |  |     if (msgobj == NULL) { | 
					
						
							|  |  |  |         failure = "error while formatting exception"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     info->msg = _copy_string_obj_raw(msgobj, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     Py_DECREF(msgobj); | 
					
						
							|  |  |  |     if (info->msg == NULL) { | 
					
						
							|  |  |  |         failure = "error while copying exception message"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     // Pickle a traceback.TracebackException.
 | 
					
						
							|  |  |  |     PyObject *tbexc = NULL; | 
					
						
							|  |  |  |     if (_convert_exc_to_TracebackException(exc, &tbexc) < 0) { | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |         PyErr_FormatUnraisable("Exception ignored while creating TracebackException"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         PyErr_Clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |         info->errdisplay = _format_TracebackException(tbexc); | 
					
						
							|  |  |  |         Py_DECREF(tbexc); | 
					
						
							|  |  |  |         if (info->errdisplay == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #ifdef Py_DEBUG
 | 
					
						
							| 
									
										
										
										
											2024-08-01 09:26:09 +09:00
										 |  |  |             PyErr_FormatUnraisable("Exception ignored while formatting TracebackException"); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     assert(failure != NULL); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     _PyXI_excinfo_clear(info); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return failure; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | static const char * | 
					
						
							|  |  |  | _PyXI_excinfo_InitFromObject(_PyXI_excinfo *info, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *exctype = PyObject_GetAttrString(obj, "type"); | 
					
						
							|  |  |  |     if (exctype == NULL) { | 
					
						
							|  |  |  |         failure = "exception snapshot missing 'type' attribute"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = _excinfo_init_type_from_object(&info->type, exctype); | 
					
						
							|  |  |  |     Py_DECREF(exctype); | 
					
						
							|  |  |  |     if (res < 0) { | 
					
						
							|  |  |  |         failure = "error while initializing exception type snapshot"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Extract the exception message.
 | 
					
						
							|  |  |  |     PyObject *msgobj = PyObject_GetAttrString(obj, "msg"); | 
					
						
							|  |  |  |     if (msgobj == NULL) { | 
					
						
							|  |  |  |         failure = "exception snapshot missing 'msg' attribute"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     info->msg = _copy_string_obj_raw(msgobj, NULL); | 
					
						
							|  |  |  |     Py_DECREF(msgobj); | 
					
						
							|  |  |  |     if (info->msg == NULL) { | 
					
						
							|  |  |  |         failure = "error while copying exception message"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Pickle a traceback.TracebackException.
 | 
					
						
							|  |  |  |     PyObject *errdisplay = PyObject_GetAttrString(obj, "errdisplay"); | 
					
						
							|  |  |  |     if (errdisplay == NULL) { | 
					
						
							|  |  |  |         failure = "exception snapshot missing 'errdisplay' attribute"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     info->errdisplay = _copy_string_obj_raw(errdisplay, NULL); | 
					
						
							|  |  |  |     Py_DECREF(errdisplay); | 
					
						
							|  |  |  |     if (info->errdisplay == NULL) { | 
					
						
							|  |  |  |         failure = "error while copying exception error display"; | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     assert(failure != NULL); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     _PyXI_excinfo_clear(info); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     return failure; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     PyObject *tbexc = NULL; | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         tbexc = PyUnicode_FromString(info->errdisplay); | 
					
						
							|  |  |  |         if (tbexc == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         else { | 
					
						
							|  |  |  |             PyErr_SetObject(exctype, tbexc); | 
					
						
							|  |  |  |             Py_DECREF(tbexc); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     PyObject *formatted = _PyXI_excinfo_format(info); | 
					
						
							|  |  |  |     PyErr_SetObject(exctype, formatted); | 
					
						
							|  |  |  |     Py_DECREF(formatted); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (tbexc != NULL) { | 
					
						
							|  |  |  |         PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |         if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #ifdef Py_DEBUG
 | 
					
						
							| 
									
										
										
										
											2025-01-31 09:45:35 +01:00
										 |  |  |             PyErr_FormatUnraisable("Exception ignored while " | 
					
						
							|  |  |  |                                    "setting _errdisplay"); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         Py_DECREF(tbexc); | 
					
						
							|  |  |  |         PyErr_SetRaisedException(exc); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | _PyXI_excinfo_TypeAsObject(_PyXI_excinfo *info) | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     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; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         int res = PyObject_SetAttrString(ns, "__name__", name); | 
					
						
							|  |  |  |         Py_DECREF(name); | 
					
						
							|  |  |  |         if (res < 0) { | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |         empty = 0; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     if (info->errdisplay != NULL) { | 
					
						
							|  |  |  |         PyObject *tbexc = PyUnicode_FromString(info->errdisplay); | 
					
						
							|  |  |  |         if (tbexc == NULL) { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             PyErr_Clear(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |             res = PyObject_SetAttrString(ns, "errdisplay", tbexc); | 
					
						
							| 
									
										
										
										
											2023-12-12 17:00:54 -07:00
										 |  |  |             Py_DECREF(tbexc); | 
					
						
							|  |  |  |             if (res < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return ns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     Py_DECREF(ns); | 
					
						
							|  |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-06 11:09:22 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | _PyXI_excinfo * | 
					
						
							|  |  |  | _PyXI_NewExcInfo(PyObject *exc) | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(!PyErr_Occurred()); | 
					
						
							|  |  |  |     if (exc == NULL || exc == Py_None) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "missing exc"); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyXI_excinfo *info = PyMem_RawCalloc(1, sizeof(_PyXI_excinfo)); | 
					
						
							|  |  |  |     if (info == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     const char *failure; | 
					
						
							|  |  |  |     if (PyExceptionInstance_Check(exc) || PyExceptionClass_Check(exc)) { | 
					
						
							|  |  |  |         failure = _PyXI_excinfo_InitFromException(info, exc); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         failure = _PyXI_excinfo_InitFromObject(info, exc); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         PyMem_RawFree(info); | 
					
						
							|  |  |  |         set_exc_with_cause(PyExc_Exception, failure); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     return info; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FreeExcInfo(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyXI_excinfo_clear(info); | 
					
						
							|  |  |  |     PyMem_RawFree(info); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyXI_FormatExcInfo(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _PyXI_excinfo_format(info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyXI_ExcInfoAsObject(_PyXI_excinfo *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return _PyXI_excinfo_AsObject(info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /***************************/ | 
					
						
							|  |  |  | /* short-term data sharing */ | 
					
						
							|  |  |  | /***************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* error codes */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     assert(!PyErr_Occurred()); | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |     assert(code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |     assert(code != _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     switch (code) { | 
					
						
							|  |  |  |     case _PyXI_ERR_OTHER: | 
					
						
							|  |  |  |         // XXX msg?
 | 
					
						
							| 
									
										
										
										
											2024-04-03 10:58:39 -06:00
										 |  |  |         PyErr_SetNone(PyExc_InterpreterError); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_NO_MEMORY: | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_ALREADY_RUNNING: | 
					
						
							|  |  |  |         assert(interp != NULL); | 
					
						
							| 
									
										
										
										
											2024-11-18 17:11:12 -07:00
										 |  |  |         _PyErr_SetInterpreterAlreadyRunning(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_MAIN_NS_FAILURE: | 
					
						
							| 
									
										
										
										
											2024-04-03 10:58:39 -06:00
										 |  |  |         PyErr_SetString(PyExc_InterpreterError, | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |                         "failed to get __main__ namespace"); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_APPLY_NS_FAILURE: | 
					
						
							| 
									
										
										
										
											2024-04-03 10:58:39 -06:00
										 |  |  |         PyErr_SetString(PyExc_InterpreterError, | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |                         "failed to apply namespace to __main__"); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     case _PyXI_ERR_PRESERVE_FAILURE: | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_InterpreterError, | 
					
						
							|  |  |  |                         "failed to preserve objects across session"); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case _PyXI_ERR_EXC_PROPAGATION_FAILURE: | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_InterpreterError, | 
					
						
							|  |  |  |                         "failed to transfer exception between interpreters"); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     case _PyXI_ERR_NOT_SHAREABLE: | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         _set_xid_lookup_failure(tstate, NULL, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							| 
									
										
										
										
											2025-05-21 07:23:48 -06:00
										 |  |  |         Py_FatalError("unsupported error code"); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | #else
 | 
					
						
							|  |  |  |         PyErr_Format(PyExc_RuntimeError, "unsupported error code %d", code); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(PyErr_Occurred()); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | /* basic failure info */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct xi_failure { | 
					
						
							|  |  |  |     // The kind of error to propagate.
 | 
					
						
							|  |  |  |     _PyXI_errcode code; | 
					
						
							|  |  |  |     // The propagated message.
 | 
					
						
							|  |  |  |     const char *msg; | 
					
						
							|  |  |  |     int msg_owned; | 
					
						
							|  |  |  | };  // _PyXI_failure
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define XI_FAILURE_INIT (_PyXI_failure){ .code = _PyXI_ERR_NO_ERROR }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | clear_xi_failure(_PyXI_failure *failure) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (failure->msg != NULL && failure->msg_owned) { | 
					
						
							|  |  |  |         PyMem_RawFree((void*)failure->msg); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *failure = XI_FAILURE_INIT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | copy_xi_failure(_PyXI_failure *dest, _PyXI_failure *src) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     *dest = *src; | 
					
						
							|  |  |  |     dest->msg_owned = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyXI_failure * | 
					
						
							|  |  |  | _PyXI_NewFailure(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyXI_failure *failure = PyMem_RawMalloc(sizeof(_PyXI_failure)); | 
					
						
							|  |  |  |     if (failure == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *failure = XI_FAILURE_INIT; | 
					
						
							|  |  |  |     return failure; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FreeFailure(_PyXI_failure *failure) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     clear_xi_failure(failure); | 
					
						
							|  |  |  |     PyMem_RawFree(failure); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyXI_errcode | 
					
						
							|  |  |  | _PyXI_GetFailureCode(_PyXI_failure *failure) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (failure == NULL) { | 
					
						
							|  |  |  |         return _PyXI_ERR_NO_ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return failure->code; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_InitFailureUTF8(_PyXI_failure *failure, | 
					
						
							|  |  |  |                       _PyXI_errcode code, const char *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     *failure = (_PyXI_failure){ | 
					
						
							|  |  |  |         .code = code, | 
					
						
							|  |  |  |         .msg = msg, | 
					
						
							|  |  |  |         .msg_owned = 0, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyXI_InitFailure(_PyXI_failure *failure, _PyXI_errcode code, PyObject *obj) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-10-19 22:24:28 +03:00
										 |  |  |     *failure = (_PyXI_failure){ | 
					
						
							|  |  |  |         .code = code, | 
					
						
							|  |  |  |         .msg = NULL, | 
					
						
							|  |  |  |         .msg_owned = 0, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (obj == NULL) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyObject *msgobj = PyObject_Str(obj); | 
					
						
							|  |  |  |     if (msgobj == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // This will leak if not paired with clear_xi_failure().
 | 
					
						
							|  |  |  |     // That happens automatically in _capture_current_exception().
 | 
					
						
							|  |  |  |     const char *msg = _copy_string_obj_raw(msgobj, NULL); | 
					
						
							|  |  |  |     Py_DECREF(msgobj); | 
					
						
							| 
									
										
										
										
											2025-10-19 22:24:28 +03:00
										 |  |  |     if (msg == NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *failure = (_PyXI_failure){ | 
					
						
							|  |  |  |         .code = code, | 
					
						
							|  |  |  |         .msg = msg, | 
					
						
							|  |  |  |         .msg_owned = 1, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /* shared exceptions */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | typedef struct { | 
					
						
							|  |  |  |     // The originating interpreter.
 | 
					
						
							|  |  |  |     PyInterpreterState *interp; | 
					
						
							|  |  |  |     // The error to propagate, if different from the uncaught exception.
 | 
					
						
							|  |  |  |     _PyXI_failure *override; | 
					
						
							|  |  |  |     _PyXI_failure _override; | 
					
						
							|  |  |  |     // The exception information to propagate, if applicable.
 | 
					
						
							|  |  |  |     // This is populated only for some error codes,
 | 
					
						
							|  |  |  |     // but always for _PyXI_ERR_UNCAUGHT_EXCEPTION.
 | 
					
						
							|  |  |  |     _PyXI_excinfo uncaught; | 
					
						
							|  |  |  | } _PyXI_error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | xi_error_clear(_PyXI_error *err) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     err->interp = NULL; | 
					
						
							|  |  |  |     if (err->override != NULL) { | 
					
						
							|  |  |  |         clear_xi_failure(err->override); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     _PyXI_excinfo_clear(&err->uncaught); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | xi_error_is_set(_PyXI_error *error) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (error->override != NULL) { | 
					
						
							|  |  |  |         assert(error->override->code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |         assert(error->override->code != _PyXI_ERR_UNCAUGHT_EXCEPTION | 
					
						
							|  |  |  |                || excinfo_is_set(&error->uncaught)); | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return excinfo_is_set(&error->uncaught); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | xi_error_has_override(_PyXI_error *err) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (err->override == NULL) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return (err->override->code != _PyXI_ERR_NO_ERROR | 
					
						
							|  |  |  |             && err->override->code != _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PyObject * | 
					
						
							|  |  |  | xi_error_resolve_current_exc(PyThreadState *tstate, | 
					
						
							|  |  |  |                              _PyXI_failure *override) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(override == NULL || override->code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |     if (exc == NULL) { | 
					
						
							|  |  |  |         assert(override == NULL | 
					
						
							|  |  |  |                || override->code != _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (override == NULL) { | 
					
						
							|  |  |  |         // This is equivalent to _PyXI_ERR_UNCAUGHT_EXCEPTION.
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (override->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							|  |  |  |         // We want to actually capture the current exception.
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (exc != NULL) { | 
					
						
							|  |  |  |         // It might make sense to do similarly for other codes.
 | 
					
						
							|  |  |  |         if (override->code == _PyXI_ERR_ALREADY_RUNNING) { | 
					
						
							|  |  |  |             // We don't need the exception info.
 | 
					
						
							|  |  |  |             Py_CLEAR(exc); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // ...else we want to actually capture the current exception.
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return exc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | xi_error_set_override(PyThreadState *tstate, _PyXI_error *err, | 
					
						
							|  |  |  |                       _PyXI_failure *override) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(err->override == NULL); | 
					
						
							|  |  |  |     assert(override != NULL); | 
					
						
							|  |  |  |     assert(override->code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |     // Use xi_error_set_exc() instead of setting _PyXI_ERR_UNCAUGHT_EXCEPTION..
 | 
					
						
							|  |  |  |     assert(override->code != _PyXI_ERR_UNCAUGHT_EXCEPTION); | 
					
						
							|  |  |  |     err->override = &err->_override; | 
					
						
							|  |  |  |     // The caller still owns override->msg.
 | 
					
						
							|  |  |  |     copy_xi_failure(&err->_override, override); | 
					
						
							|  |  |  |     err->interp = tstate->interp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | xi_error_set_override_code(PyThreadState *tstate, _PyXI_error *err, | 
					
						
							|  |  |  |                            _PyXI_errcode code) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyXI_failure override = XI_FAILURE_INIT; | 
					
						
							|  |  |  |     override.code = code; | 
					
						
							|  |  |  |     xi_error_set_override(tstate, err, &override); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * | 
					
						
							|  |  |  | xi_error_set_exc(PyThreadState *tstate, _PyXI_error *err, PyObject *exc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(!_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |     assert(!xi_error_is_set(err)); | 
					
						
							|  |  |  |     assert(err->override == NULL); | 
					
						
							|  |  |  |     assert(err->interp == NULL); | 
					
						
							|  |  |  |     assert(exc != NULL); | 
					
						
							|  |  |  |     const char *failure = | 
					
						
							|  |  |  |                 _PyXI_excinfo_InitFromException(&err->uncaught, exc); | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							|  |  |  |         // We failed to initialize err->uncaught.
 | 
					
						
							|  |  |  |         // XXX Print the excobj/traceback?  Emit a warning?
 | 
					
						
							|  |  |  |         // XXX Print the current exception/traceback?
 | 
					
						
							|  |  |  |         if (_PyErr_ExceptionMatches(tstate, PyExc_MemoryError)) { | 
					
						
							|  |  |  |             xi_error_set_override_code(tstate, err, _PyXI_ERR_NO_MEMORY); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             xi_error_set_override_code(tstate, err, _PyXI_ERR_OTHER); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         PyErr_Clear(); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return failure; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | static PyObject * | 
					
						
							|  |  |  | _PyXI_ApplyError(_PyXI_error *error, const char *failure) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     PyThreadState *tstate = PyThreadState_Get(); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (failure != NULL) { | 
					
						
							|  |  |  |         xi_error_clear(error); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _PyXI_errcode code = _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							|  |  |  |     if (error->override != NULL) { | 
					
						
							|  |  |  |         code = error->override->code; | 
					
						
							|  |  |  |         assert(code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         // We will raise an exception that proxies the propagated exception.
 | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |        return _PyXI_excinfo_AsObject(&error->uncaught); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     else if (code == _PyXI_ERR_NOT_SHAREABLE) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // Propagate the exception directly.
 | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |         assert(!_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         PyObject *cause = NULL; | 
					
						
							|  |  |  |         if (excinfo_is_set(&error->uncaught)) { | 
					
						
							|  |  |  |             // Maybe instead set a PyExc_ExceptionSnapshot as __cause__?
 | 
					
						
							|  |  |  |             // That type doesn't exist currently
 | 
					
						
							|  |  |  |             // but would look like interpreters.ExecutionFailed.
 | 
					
						
							|  |  |  |             _PyXI_excinfo_Apply(&error->uncaught, PyExc_Exception); | 
					
						
							|  |  |  |             cause = _PyErr_GetRaisedException(tstate); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const char *msg = error->override != NULL | 
					
						
							|  |  |  |             ? error->override->msg | 
					
						
							|  |  |  |             : error->uncaught.msg; | 
					
						
							|  |  |  |         _set_xid_lookup_failure(tstate, NULL, msg, cause); | 
					
						
							|  |  |  |         Py_XDECREF(cause); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         // Raise an exception corresponding to the code.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         (void)_PyXI_ApplyErrorCode(code, error->interp); | 
					
						
							|  |  |  |         assert(error->override == NULL || error->override->msg == NULL); | 
					
						
							|  |  |  |         if (excinfo_is_set(&error->uncaught)) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             // __context__ will be set to a proxy of the propagated exception.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             // (or use PyExc_ExceptionSnapshot like _PyXI_ERR_NOT_SHAREABLE?)
 | 
					
						
							|  |  |  |             PyObject *exc = _PyErr_GetRaisedException(tstate); | 
					
						
							| 
									
										
										
										
											2024-04-03 10:58:39 -06:00
										 |  |  |             _PyXI_excinfo_Apply(&error->uncaught, PyExc_InterpreterError); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             PyObject *exc2 = _PyErr_GetRaisedException(tstate); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |             PyException_SetContext(exc, exc2); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             _PyErr_SetRaisedException(tstate, exc); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(PyErr_Occurred()); | 
					
						
							| 
									
										
										
										
											2023-11-22 17:55:00 -07:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* shared namespaces */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | /* Shared namespaces are expected to have relatively short lifetimes.
 | 
					
						
							|  |  |  |    This means dealloc of a shared namespace will normally happen "soon". | 
					
						
							|  |  |  |    Namespace items hold cross-interpreter data, which must get released. | 
					
						
							|  |  |  |    If the namespace/items are cleared in a different interpreter than | 
					
						
							|  |  |  |    where the items' cross-interpreter data was set then that will cause | 
					
						
							|  |  |  |    pending calls to be used to release the cross-interpreter data. | 
					
						
							|  |  |  |    The tricky bit is that the pending calls can happen sufficiently | 
					
						
							|  |  |  |    later that the namespace/items might already be deallocated.  This is | 
					
						
							|  |  |  |    a problem if the cross-interpreter data is allocated as part of a | 
					
						
							|  |  |  |    namespace item.  If that's the case then we must ensure the shared | 
					
						
							|  |  |  |    namespace is only cleared/freed *after* that data has been released. */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | typedef struct _sharednsitem { | 
					
						
							|  |  |  |     const char *name; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_t *xidata; | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     // We could have a "PyXIData _data" field, so it would
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     // be allocated as part of the item and avoid an extra allocation.
 | 
					
						
							|  |  |  |     // However, doing so adds a bunch of complexity because we must
 | 
					
						
							|  |  |  |     // ensure the item isn't freed before a pending call might happen
 | 
					
						
							|  |  |  |     // in a different interpreter to release the XI data.
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } _PyXI_namespace_item; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 11:24:09 -06:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_is_initialized(_PyXI_namespace_item *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->name != NULL) { | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-05-22 11:24:09 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharednsitem_init(_PyXI_namespace_item *item, PyObject *key) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-12-12 17:31:30 -07:00
										 |  |  |     item->name = _copy_string_obj_raw(key, NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     if (item->name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         assert(!_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     item->xidata = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     assert(_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if (item->xidata == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (p_interpid != NULL) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         *p_interpid = _PyXIData_INTERPID(item->xidata); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value, | 
					
						
							|  |  |  |                         xidata_fallback_t fallback) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     assert(_sharednsitem_is_initialized(item)); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(item->xidata == NULL); | 
					
						
							|  |  |  |     item->xidata = _PyXIData_New(); | 
					
						
							|  |  |  |     if (item->xidata == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     PyThreadState *tstate = PyThreadState_Get(); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     if (_PyObject_GetXIData(tstate, value, fallback, item->xidata) < 0) { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         PyMem_RawFree(item->xidata); | 
					
						
							|  |  |  |         item->xidata = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // The caller may want to propagate PyExc_NotShareableError
 | 
					
						
							|  |  |  |         // if currently switched between interpreters.
 | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | _sharednsitem_clear_value(_PyXI_namespace_item *item) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     _PyXIData_t *xidata = item->xidata; | 
					
						
							|  |  |  |     if (xidata != NULL) { | 
					
						
							|  |  |  |         item->xidata = NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         int rawfree = 1; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |         (void)_release_xid_data(xidata, rawfree); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharednsitem_clear(_PyXI_namespace_item *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (item->name != NULL) { | 
					
						
							|  |  |  |         PyMem_RawFree((void *)item->name); | 
					
						
							|  |  |  |         item->name = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     _sharednsitem_clear_value(item); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns, | 
					
						
							|  |  |  |                            xidata_fallback_t fallback) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(item->name != NULL); | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     assert(item->xidata == NULL); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     PyObject *value = PyDict_GetItemString(ns, item->name);  // borrowed
 | 
					
						
							|  |  |  |     if (value == NULL) { | 
					
						
							|  |  |  |         if (PyErr_Occurred()) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // When applied, this item will be set to the default (or fail).
 | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     if (_sharednsitem_set_value(item, value, fallback) < 0) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _sharednsitem_apply(_PyXI_namespace_item *item, PyObject *ns, PyObject *dflt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *name = PyUnicode_FromString(item->name); | 
					
						
							|  |  |  |     if (name == NULL) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyObject *value; | 
					
						
							| 
									
										
										
										
											2025-04-28 11:55:15 -06:00
										 |  |  |     if (item->xidata != NULL) { | 
					
						
							|  |  |  |         value = _PyXIData_NewObject(item->xidata); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         if (value == NULL) { | 
					
						
							|  |  |  |             Py_DECREF(name); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         value = Py_NewRef(dflt); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = PyDict_SetItem(ns, name, value); | 
					
						
							|  |  |  |     Py_DECREF(name); | 
					
						
							|  |  |  |     Py_DECREF(value); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | typedef struct { | 
					
						
							|  |  |  |     Py_ssize_t maxitems; | 
					
						
							|  |  |  |     Py_ssize_t numnames; | 
					
						
							|  |  |  |     Py_ssize_t numvalues; | 
					
						
							|  |  |  |     _PyXI_namespace_item items[1]; | 
					
						
							|  |  |  | } _PyXI_namespace; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | _sharedns_check_counts(_PyXI_namespace *ns) | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (ns->maxitems <= 0) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ns->numnames < 0) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ns->numnames > ns->maxitems) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ns->numvalues < 0) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ns->numvalues > ns->numnames) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | _sharedns_check_consistency(_PyXI_namespace *ns) | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (!_sharedns_check_counts(ns)) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Py_ssize_t i = 0; | 
					
						
							|  |  |  |     _PyXI_namespace_item *item; | 
					
						
							|  |  |  |     if (ns->numvalues > 0) { | 
					
						
							|  |  |  |         item = &ns->items[0]; | 
					
						
							|  |  |  |         if (!_sharednsitem_is_initialized(item)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int64_t interpid0 = -1; | 
					
						
							|  |  |  |         if (!_sharednsitem_has_value(item, &interpid0)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         i += 1; | 
					
						
							|  |  |  |         for (; i < ns->numvalues; i++) { | 
					
						
							|  |  |  |             item = &ns->items[i]; | 
					
						
							|  |  |  |             if (!_sharednsitem_is_initialized(item)) { | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             int64_t interpid = -1; | 
					
						
							|  |  |  |             if (!_sharednsitem_has_value(item, &interpid)) { | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (interpid != interpid0) { | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     for (; i < ns->numnames; i++) { | 
					
						
							|  |  |  |         item = &ns->items[i]; | 
					
						
							|  |  |  |         if (!_sharednsitem_is_initialized(item)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         if (_sharednsitem_has_value(item, NULL)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     for (; i < ns->maxitems; i++) { | 
					
						
							|  |  |  |         item = &ns->items[i]; | 
					
						
							|  |  |  |         if (_sharednsitem_is_initialized(item)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (_sharednsitem_has_value(item, NULL)) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static _PyXI_namespace * | 
					
						
							|  |  |  | _sharedns_alloc(Py_ssize_t maxitems) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (maxitems < 0) { | 
					
						
							|  |  |  |         if (!PyErr_Occurred()) { | 
					
						
							|  |  |  |             PyErr_BadInternalCall(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (maxitems == 0) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_ValueError, "empty namespaces not allowed"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     // Check for overflow.
 | 
					
						
							|  |  |  |     size_t fixedsize = sizeof(_PyXI_namespace) - sizeof(_PyXI_namespace_item); | 
					
						
							|  |  |  |     if ((size_t)maxitems > | 
					
						
							|  |  |  |         ((size_t)PY_SSIZE_T_MAX - fixedsize) / sizeof(_PyXI_namespace_item)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Allocate the value, including items.
 | 
					
						
							|  |  |  |     size_t size = fixedsize + sizeof(_PyXI_namespace_item) * maxitems; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _PyXI_namespace *ns = PyMem_RawCalloc(size, 1); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     ns->maxitems = maxitems; | 
					
						
							|  |  |  |     assert(_sharedns_check_consistency(ns)); | 
					
						
							|  |  |  |     return ns; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _sharedns_free(_PyXI_namespace *ns) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     // If we weren't always dynamically allocating the cross-interpreter
 | 
					
						
							|  |  |  |     // data in each item then we would need to use a pending call
 | 
					
						
							|  |  |  |     // to call _sharedns_free(), to avoid the race between freeing
 | 
					
						
							|  |  |  |     // the shared namespace and releasing the XI data.
 | 
					
						
							|  |  |  |     assert(_sharedns_check_counts(ns)); | 
					
						
							|  |  |  |     Py_ssize_t i = 0; | 
					
						
							|  |  |  |     _PyXI_namespace_item *item; | 
					
						
							|  |  |  |     if (ns->numvalues > 0) { | 
					
						
							|  |  |  |         // One or more items may have interpreter-specific data.
 | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |         int64_t interpid = PyInterpreterState_GetID(PyInterpreterState_Get()); | 
					
						
							|  |  |  |         int64_t interpid_i; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |         for (; i < ns->numvalues; i++) { | 
					
						
							|  |  |  |             item = &ns->items[i]; | 
					
						
							|  |  |  |             assert(_sharednsitem_is_initialized(item)); | 
					
						
							|  |  |  |             // While we do want to ensure consistency across items,
 | 
					
						
							|  |  |  |             // technically they don't need to match the current
 | 
					
						
							|  |  |  |             // interpreter.  However, we keep the constraint for
 | 
					
						
							|  |  |  |             // simplicity, by giving _PyXI_FreeNamespace() the exclusive
 | 
					
						
							|  |  |  |             // responsibility of dealing with the owning interpreter.
 | 
					
						
							|  |  |  |             assert(_sharednsitem_has_value(item, &interpid_i)); | 
					
						
							|  |  |  |             assert(interpid_i == interpid); | 
					
						
							|  |  |  |             _sharednsitem_clear(item); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (; i < ns->numnames; i++) { | 
					
						
							|  |  |  |         item = &ns->items[i]; | 
					
						
							|  |  |  |         assert(_sharednsitem_is_initialized(item)); | 
					
						
							|  |  |  |         assert(!_sharednsitem_has_value(item, NULL)); | 
					
						
							|  |  |  |         _sharednsitem_clear(item); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     for (; i < ns->maxitems; i++) { | 
					
						
							|  |  |  |         item = &ns->items[i]; | 
					
						
							|  |  |  |         assert(!_sharednsitem_is_initialized(item)); | 
					
						
							|  |  |  |         assert(!_sharednsitem_has_value(item, NULL)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     PyMem_RawFree(ns); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static _PyXI_namespace * | 
					
						
							|  |  |  | _create_sharedns(PyObject *names) | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     assert(names != NULL); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     Py_ssize_t numnames = PyDict_CheckExact(names) | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         ? PyDict_Size(names) | 
					
						
							|  |  |  |         : PySequence_Size(names); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _PyXI_namespace *ns = _sharedns_alloc(numnames); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							|  |  |  |         return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _PyXI_namespace_item *items = ns->items; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Fill in the names.
 | 
					
						
							|  |  |  |     if (PyDict_CheckExact(names)) { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         Py_ssize_t i = 0; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         Py_ssize_t pos = 0; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         PyObject *name; | 
					
						
							|  |  |  |         while(PyDict_Next(names, &pos, &name, NULL)) { | 
					
						
							|  |  |  |             if (_sharednsitem_init(&items[i], name) < 0) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             ns->numnames += 1; | 
					
						
							|  |  |  |             i += 1; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (PySequence_Check(names)) { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         for (Py_ssize_t i = 0; i < numnames; i++) { | 
					
						
							|  |  |  |             PyObject *name = PySequence_GetItem(names, i); | 
					
						
							|  |  |  |             if (name == NULL) { | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             int res = _sharednsitem_init(&items[i], name); | 
					
						
							|  |  |  |             Py_DECREF(name); | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |             if (res < 0) { | 
					
						
							|  |  |  |                 goto error; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             ns->numnames += 1; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_NotImplementedError, | 
					
						
							|  |  |  |                         "non-sequence namespace not supported"); | 
					
						
							|  |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     assert(ns->numnames == ns->maxitems); | 
					
						
							|  |  |  |     return ns; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _sharedns_free(ns); | 
					
						
							|  |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | static void _propagate_not_shareable_error(PyThreadState *, | 
					
						
							|  |  |  |                                            _PyXI_failure *); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | _fill_sharedns(_PyXI_namespace *ns, PyObject *nsobj, | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                xidata_fallback_t fallback, _PyXI_failure *p_err) | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | { | 
					
						
							|  |  |  |     // All items are expected to be shareable.
 | 
					
						
							|  |  |  |     assert(_sharedns_check_counts(ns)); | 
					
						
							|  |  |  |     assert(ns->numnames == ns->maxitems); | 
					
						
							|  |  |  |     assert(ns->numvalues == 0); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyThreadState *tstate = PyThreadState_Get(); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     for (Py_ssize_t i=0; i < ns->maxitems; i++) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         if (_sharednsitem_copy_from_ns(&ns->items[i], nsobj, fallback) < 0) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             if (p_err != NULL) { | 
					
						
							|  |  |  |                 _propagate_not_shareable_error(tstate, p_err); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |             // Clear out the ones we set so far.
 | 
					
						
							|  |  |  |             for (Py_ssize_t j=0; j < i; j++) { | 
					
						
							|  |  |  |                 _sharednsitem_clear_value(&ns->items[j]); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |                 ns->numvalues -= 1; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         ns->numvalues += 1; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _sharedns_free_pending(void *data) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _sharedns_free((_PyXI_namespace *)data); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static void | 
					
						
							|  |  |  | _destroy_sharedns(_PyXI_namespace *ns) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(_sharedns_check_counts(ns)); | 
					
						
							|  |  |  |     assert(ns->numnames == ns->maxitems); | 
					
						
							|  |  |  |     if (ns->numvalues == 0) { | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     int64_t interpid0; | 
					
						
							|  |  |  |     if (!_sharednsitem_has_value(&ns->items[0], &interpid0)) { | 
					
						
							|  |  |  |         // This shouldn't have been possible.
 | 
					
						
							|  |  |  |         // We can deal with it in _sharedns_free().
 | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     PyInterpreterState *interp = _PyInterpreterState_LookUpID(interpid0); | 
					
						
							|  |  |  |     if (interp == PyInterpreterState_Get()) { | 
					
						
							|  |  |  |         _sharedns_free(ns); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     // One or more items may have interpreter-specific data.
 | 
					
						
							|  |  |  |     // Currently the xidata for each value is dynamically allocated,
 | 
					
						
							|  |  |  |     // so technically we don't need to worry about that.
 | 
					
						
							|  |  |  |     // However, explicitly adding a pending call here is simpler.
 | 
					
						
							|  |  |  |     (void)_Py_CallInInterpreter(interp, _sharedns_free_pending, ns); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _apply_sharedns(_PyXI_namespace *ns, PyObject *nsobj, PyObject *dflt) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     for (Py_ssize_t i=0; i < ns->maxitems; i++) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         if (_sharednsitem_apply(&ns->items[i], nsobj, dflt) != 0) { | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | /*********************************/ | 
					
						
							|  |  |  | /* switched-interpreter sessions */ | 
					
						
							|  |  |  | /*********************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct xi_session { | 
					
						
							|  |  |  | #define SESSION_UNUSED 0
 | 
					
						
							|  |  |  | #define SESSION_ACTIVE 1
 | 
					
						
							|  |  |  |     int status; | 
					
						
							|  |  |  |     int switched; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Once a session has been entered, this is the tstate that was
 | 
					
						
							|  |  |  |     // current before the session.  If it is different from cur_tstate
 | 
					
						
							|  |  |  |     // then we must have switched interpreters.  Either way, this will
 | 
					
						
							|  |  |  |     // be the current tstate once we exit the session.
 | 
					
						
							|  |  |  |     PyThreadState *prev_tstate; | 
					
						
							|  |  |  |     // Once a session has been entered, this is the current tstate.
 | 
					
						
							|  |  |  |     // It must be current when the session exits.
 | 
					
						
							|  |  |  |     PyThreadState *init_tstate; | 
					
						
							|  |  |  |     // This is true if init_tstate needs cleanup during exit.
 | 
					
						
							|  |  |  |     int own_init_tstate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // This is true if, while entering the session, init_thread took
 | 
					
						
							|  |  |  |     // "ownership" of the interpreter's __main__ module.  This means
 | 
					
						
							|  |  |  |     // it is the only thread that is allowed to run code there.
 | 
					
						
							|  |  |  |     // (Caveat: for now, users may still run exec() against the
 | 
					
						
							|  |  |  |     // __main__ module's dict, though that isn't advisable.)
 | 
					
						
							|  |  |  |     int running; | 
					
						
							|  |  |  |     // This is a cached reference to the __dict__ of the entered
 | 
					
						
							|  |  |  |     // interpreter's __main__ module.  It is looked up when at the
 | 
					
						
							|  |  |  |     // beginning of the session as a convenience.
 | 
					
						
							|  |  |  |     PyObject *main_ns; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     // This is a dict of objects that will be available (via sharing)
 | 
					
						
							|  |  |  |     // once the session exits.  Do not access this directly; use
 | 
					
						
							|  |  |  |     // _PyXI_Preserve() and _PyXI_GetPreserved() instead;
 | 
					
						
							|  |  |  |     PyObject *_preserved; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _PyXI_session * | 
					
						
							|  |  |  | _PyXI_NewSession(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     _PyXI_session *session = PyMem_RawCalloc(1, sizeof(_PyXI_session)); | 
					
						
							|  |  |  |     if (session == NULL) { | 
					
						
							|  |  |  |         PyErr_NoMemory(); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return session; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FreeSession(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(session->status == SESSION_UNUSED); | 
					
						
							|  |  |  |     PyMem_RawFree(session); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int | 
					
						
							|  |  |  | _session_is_active(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return session->status == SESSION_ACTIVE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* enter/exit a cross-interpreter session */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _enter_session(_PyXI_session *session, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Set here and cleared in _exit_session().
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     assert(session->status == SESSION_UNUSED); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     assert(!session->own_init_tstate); | 
					
						
							|  |  |  |     assert(session->init_tstate == NULL); | 
					
						
							|  |  |  |     assert(session->prev_tstate == NULL); | 
					
						
							|  |  |  |     // Set elsewhere and cleared in _exit_session().
 | 
					
						
							|  |  |  |     assert(!session->running); | 
					
						
							|  |  |  |     assert(session->main_ns == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to interpreter.
 | 
					
						
							|  |  |  |     PyThreadState *tstate = PyThreadState_Get(); | 
					
						
							|  |  |  |     PyThreadState *prev = tstate; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     int same_interp = (interp == tstate->interp); | 
					
						
							|  |  |  |     if (!same_interp) { | 
					
						
							| 
									
										
										
										
											2024-06-25 14:35:12 -06:00
										 |  |  |         tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_EXEC); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // XXX Possible GILState issues?
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         PyThreadState *swapped = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |         assert(swapped == prev); | 
					
						
							|  |  |  |         (void)swapped; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     *session = (_PyXI_session){ | 
					
						
							|  |  |  |         .status = SESSION_ACTIVE, | 
					
						
							|  |  |  |         .switched = !same_interp, | 
					
						
							|  |  |  |         .init_tstate = tstate, | 
					
						
							|  |  |  |         .prev_tstate = prev, | 
					
						
							|  |  |  |         .own_init_tstate = !same_interp, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | _exit_session(_PyXI_session *session) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *tstate = session->init_tstate; | 
					
						
							|  |  |  |     assert(tstate != NULL); | 
					
						
							|  |  |  |     assert(PyThreadState_Get() == tstate); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     assert(!_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Release any of the entered interpreters resources.
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     Py_CLEAR(session->main_ns); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     Py_CLEAR(session->_preserved); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Ensure this thread no longer owns __main__.
 | 
					
						
							|  |  |  |     if (session->running) { | 
					
						
							|  |  |  |         _PyInterpreterState_SetNotRunningMain(tstate->interp); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         assert(!_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         session->running = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch back.
 | 
					
						
							|  |  |  |     assert(session->prev_tstate != NULL); | 
					
						
							|  |  |  |     if (session->prev_tstate != session->init_tstate) { | 
					
						
							|  |  |  |         assert(session->own_init_tstate); | 
					
						
							|  |  |  |         session->own_init_tstate = 0; | 
					
						
							|  |  |  |         PyThreadState_Clear(tstate); | 
					
						
							|  |  |  |         PyThreadState_Swap(session->prev_tstate); | 
					
						
							|  |  |  |         PyThreadState_Delete(tstate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(!session->own_init_tstate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     *session = (_PyXI_session){0}; | 
					
						
							| 
									
										
										
										
											2023-11-02 18:45:42 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | static void | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | _propagate_not_shareable_error(PyThreadState *tstate, | 
					
						
							|  |  |  |                                _PyXI_failure *override) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(override != NULL); | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     PyObject *exctype = get_notshareableerror_type(tstate); | 
					
						
							|  |  |  |     if (exctype == NULL) { | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         PyErr_FormatUnraisable( | 
					
						
							|  |  |  |                 "Exception ignored while propagating not shareable error"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-04-25 14:43:38 -06:00
										 |  |  |     if (PyErr_ExceptionMatches(exctype)) { | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         // We want to propagate the exception directly.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         *override = (_PyXI_failure){ | 
					
						
							|  |  |  |             .code = _PyXI_ERR_NOT_SHAREABLE, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int _ensure_main_ns(_PyXI_session *, _PyXI_failure *); | 
					
						
							|  |  |  | static const char * capture_session_error(_PyXI_session *, _PyXI_error *, | 
					
						
							|  |  |  |                                           _PyXI_failure *); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | int | 
					
						
							|  |  |  | _PyXI_Enter(_PyXI_session *session, | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             PyInterpreterState *interp, PyObject *nsupdates, | 
					
						
							|  |  |  |             _PyXI_session_result *result) | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-16 19:12:20 +03:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     PyThreadState *tstate = _PyThreadState_GET();  // Only used for asserts
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     // Convert the attrs for cross-interpreter use.
 | 
					
						
							|  |  |  |     _PyXI_namespace *sharedns = NULL; | 
					
						
							|  |  |  |     if (nsupdates != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-24 14:53:14 -04:00
										 |  |  |         assert(PyDict_Check(nsupdates)); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         Py_ssize_t len = PyDict_Size(nsupdates); | 
					
						
							|  |  |  |         if (len < 0) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             if (result != NULL) { | 
					
						
							|  |  |  |                 result->errcode = _PyXI_ERR_APPLY_NS_FAILURE; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (len > 0) { | 
					
						
							|  |  |  |             sharedns = _create_sharedns(nsupdates); | 
					
						
							|  |  |  |             if (sharedns == NULL) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 if (result != NULL) { | 
					
						
							|  |  |  |                     result->errcode = _PyXI_ERR_APPLY_NS_FAILURE; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |                 return -1; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             // For now we limit it to shareable objects.
 | 
					
						
							|  |  |  |             xidata_fallback_t fallback = _PyXIDATA_XIDATA_ONLY; | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             _PyXI_failure _err = XI_FAILURE_INIT; | 
					
						
							|  |  |  |             if (_fill_sharedns(sharedns, nsupdates, fallback, &_err) < 0) { | 
					
						
							|  |  |  |                 assert(_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |                 if (_err.code == _PyXI_ERR_NO_ERROR) { | 
					
						
							|  |  |  |                     _err.code = _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |                 _destroy_sharedns(sharedns); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 if (result != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                     assert(_err.msg == NULL); | 
					
						
							|  |  |  |                     result->errcode = _err.code; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |                 return -1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Switch to the requested interpreter (if necessary).
 | 
					
						
							|  |  |  |     _enter_session(session, interp); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     _PyXI_failure override = XI_FAILURE_INIT; | 
					
						
							|  |  |  |     override.code = _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							| 
									
										
										
										
											2025-06-16 19:12:20 +03:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     tstate = _PyThreadState_GET(); | 
					
						
							| 
									
										
										
										
											2025-06-16 19:12:20 +03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Ensure this thread owns __main__.
 | 
					
						
							|  |  |  |     if (_PyInterpreterState_SetRunningMain(interp) < 0) { | 
					
						
							|  |  |  |         // In the case where we didn't switch interpreters, it would
 | 
					
						
							|  |  |  |         // be more efficient to leave the exception in place and return
 | 
					
						
							|  |  |  |         // immediately.  However, life is simpler if we don't.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         override.code = _PyXI_ERR_ALREADY_RUNNING; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         goto error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     session->running = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Apply the cross-interpreter data.
 | 
					
						
							|  |  |  |     if (sharedns != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         if (_ensure_main_ns(session, &override) < 0) { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (_apply_sharedns(sharedns, session->main_ns, NULL) < 0) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             override.code = _PyXI_ERR_APPLY_NS_FAILURE; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |             goto error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         _destroy_sharedns(sharedns); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     override.code = _PyXI_ERR_NO_ERROR; | 
					
						
							|  |  |  |     assert(!_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     // We want to propagate all exceptions here directly (best effort).
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(override.code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |     _PyXI_error err = {0}; | 
					
						
							|  |  |  |     const char *failure = capture_session_error(session, &err, &override); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Exit the session.
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _exit_session(session); | 
					
						
							| 
									
										
										
										
											2025-06-16 19:12:20 +03:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     tstate = _PyThreadState_GET(); | 
					
						
							| 
									
										
										
										
											2025-06-16 19:12:20 +03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (sharedns != NULL) { | 
					
						
							|  |  |  |         _destroy_sharedns(sharedns); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Apply the error from the other interpreter.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyObject *excinfo = _PyXI_ApplyError(&err, failure); | 
					
						
							|  |  |  |     xi_error_clear(&err); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     if (excinfo != NULL) { | 
					
						
							|  |  |  |         if (result != NULL) { | 
					
						
							|  |  |  |             result->excinfo = excinfo; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |             fprintf(stderr, "_PyXI_Enter(): uncaught exception discarded"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             Py_DECREF(excinfo); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | static int _pop_preserved(_PyXI_session *, _PyXI_namespace **, PyObject **, | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                           _PyXI_failure *); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | static int _finish_preserved(_PyXI_namespace *, PyObject **); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | _PyXI_Exit(_PyXI_session *session, _PyXI_failure *override, | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |            _PyXI_session_result *result) | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyThreadState *tstate = _PyThreadState_GET(); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     int res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Capture the raised exception, if any.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     _PyXI_error err = {0}; | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  |     if (override != NULL && override->code == _PyXI_ERR_NO_ERROR) { | 
					
						
							|  |  |  |         assert(override->msg == NULL); | 
					
						
							|  |  |  |         override = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |         failure = capture_session_error(session, &err, override); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         assert(override == NULL); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Capture the preserved namespace.
 | 
					
						
							|  |  |  |     _PyXI_namespace *preserved = NULL; | 
					
						
							|  |  |  |     PyObject *preservedobj = NULL; | 
					
						
							|  |  |  |     if (result != NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         assert(!_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |         _PyXI_failure _override = XI_FAILURE_INIT; | 
					
						
							|  |  |  |         if (_pop_preserved( | 
					
						
							|  |  |  |                     session, &preserved, &preservedobj, &_override) < 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             assert(preserved == NULL); | 
					
						
							|  |  |  |             assert(preservedobj == NULL); | 
					
						
							|  |  |  |             if (xi_error_is_set(&err)) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 // XXX Chain the exception (i.e. set __context__)?
 | 
					
						
							|  |  |  |                 PyErr_FormatUnraisable( | 
					
						
							|  |  |  |                     "Exception ignored while capturing preserved objects"); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                 clear_xi_failure(&_override); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                 if (_override.code == _PyXI_ERR_NO_ERROR) { | 
					
						
							|  |  |  |                     _override.code = _PyXI_ERR_UNCAUGHT_EXCEPTION; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 failure = capture_session_error(session, &err, &_override); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Exit the session.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(!_PyErr_Occurred(tstate)); | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     _exit_session(session); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     tstate = _PyThreadState_GET(); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Restore the preserved namespace.
 | 
					
						
							|  |  |  |     assert(preserved == NULL || preservedobj == NULL); | 
					
						
							|  |  |  |     if (_finish_preserved(preserved, &preservedobj) < 0) { | 
					
						
							|  |  |  |         assert(preservedobj == NULL); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         if (xi_error_is_set(&err)) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             // XXX Chain the exception (i.e. set __context__)?
 | 
					
						
							|  |  |  |             PyErr_FormatUnraisable( | 
					
						
							|  |  |  |                 "Exception ignored while capturing preserved objects"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             xi_error_set_override_code( | 
					
						
							|  |  |  |                             tstate, &err, _PyXI_ERR_PRESERVE_FAILURE); | 
					
						
							|  |  |  |             _propagate_not_shareable_error(tstate, err.override); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (result != NULL) { | 
					
						
							|  |  |  |         result->preserved = preservedobj; | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         result->errcode = err.override != NULL | 
					
						
							|  |  |  |             ? err.override->code | 
					
						
							|  |  |  |             : _PyXI_ERR_NO_ERROR; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Apply the error from the other interpreter, if any.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     if (xi_error_is_set(&err)) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         res = -1; | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         assert(!_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |         PyObject *excinfo = _PyXI_ApplyError(&err, failure); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         if (excinfo == NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             assert(_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |             if (result != NULL && !xi_error_has_override(&err)) { | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |                 _PyXI_ClearResult(result); | 
					
						
							|  |  |  |                 *result = (_PyXI_session_result){ | 
					
						
							|  |  |  |                     .errcode = _PyXI_ERR_EXC_PROPAGATION_FAILURE, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (result != NULL) { | 
					
						
							|  |  |  |             result->excinfo = excinfo; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  | #ifdef Py_DEBUG
 | 
					
						
							|  |  |  |             fprintf(stderr, "_PyXI_Exit(): uncaught exception discarded"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             Py_DECREF(excinfo); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         xi_error_clear(&err); | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return res; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* in an active cross-interpreter session */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | static const char * | 
					
						
							|  |  |  | capture_session_error(_PyXI_session *session, _PyXI_error *err, | 
					
						
							|  |  |  |                       _PyXI_failure *override) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(_session_is_active(session)); | 
					
						
							|  |  |  |     assert(!xi_error_is_set(err)); | 
					
						
							|  |  |  |     PyThreadState *tstate = session->init_tstate; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     // Normalize the exception override.
 | 
					
						
							|  |  |  |     if (override != NULL) { | 
					
						
							|  |  |  |         if (override->code == _PyXI_ERR_UNCAUGHT_EXCEPTION) { | 
					
						
							|  |  |  |             assert(override->msg == NULL); | 
					
						
							|  |  |  |             override = NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             assert(override->code != _PyXI_ERR_NO_ERROR); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     // Handle the exception, if any.
 | 
					
						
							|  |  |  |     const char *failure = NULL; | 
					
						
							|  |  |  |     PyObject *exc = xi_error_resolve_current_exc(tstate, override); | 
					
						
							|  |  |  |     if (exc != NULL) { | 
					
						
							|  |  |  |         // There is an unhandled exception we need to preserve.
 | 
					
						
							|  |  |  |         failure = xi_error_set_exc(tstate, err, exc); | 
					
						
							|  |  |  |         Py_DECREF(exc); | 
					
						
							|  |  |  |         if (_PyErr_Occurred(tstate)) { | 
					
						
							|  |  |  |             PyErr_FormatUnraisable(failure); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     // Handle the override.
 | 
					
						
							|  |  |  |     if (override != NULL && failure == NULL) { | 
					
						
							|  |  |  |         xi_error_set_override(tstate, err, override); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Finished!
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     assert(!_PyErr_Occurred(tstate)); | 
					
						
							|  |  |  |     return failure; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | _ensure_main_ns(_PyXI_session *session, _PyXI_failure *failure) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     assert(_session_is_active(session)); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyThreadState *tstate = session->init_tstate; | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (session->main_ns != NULL) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     // Cache __main__.__dict__.
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     PyObject *main_mod = _Py_GetMainModule(tstate); | 
					
						
							| 
									
										
										
										
											2025-04-28 12:46:22 -06:00
										 |  |  |     if (_Py_CheckMainModule(main_mod) < 0) { | 
					
						
							| 
									
										
										
										
											2025-06-17 13:16:59 -06:00
										 |  |  |         Py_XDECREF(main_mod); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         if (failure != NULL) { | 
					
						
							|  |  |  |             *failure = (_PyXI_failure){ | 
					
						
							|  |  |  |                 .code = _PyXI_ERR_MAIN_NS_FAILURE, | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     PyObject *ns = PyModule_GetDict(main_mod);  // borrowed
 | 
					
						
							|  |  |  |     Py_DECREF(main_mod); | 
					
						
							|  |  |  |     if (ns == NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         if (failure != NULL) { | 
					
						
							|  |  |  |             *failure = (_PyXI_failure){ | 
					
						
							|  |  |  |                 .code = _PyXI_ERR_MAIN_NS_FAILURE, | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         return -1; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     session->main_ns = Py_NewRef(ns); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  | PyObject * | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | _PyXI_GetMainNamespace(_PyXI_session *session, _PyXI_failure *failure) | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |     if (!_session_is_active(session)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, "session not active"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     if (_ensure_main_ns(session, failure) < 0) { | 
					
						
							| 
									
										
										
										
											2025-05-22 10:14:04 -06:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return session->main_ns; | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | static int | 
					
						
							|  |  |  | _pop_preserved(_PyXI_session *session, | 
					
						
							|  |  |  |                _PyXI_namespace **p_xidata, PyObject **p_obj, | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                _PyXI_failure *p_failure) | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     _PyXI_failure failure = XI_FAILURE_INIT; | 
					
						
							|  |  |  |     _PyXI_namespace *xidata = NULL; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     assert(_PyThreadState_GET() == session->init_tstate);  // active session
 | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     if (session->_preserved == NULL) { | 
					
						
							|  |  |  |         *p_xidata = NULL; | 
					
						
							|  |  |  |         *p_obj = NULL; | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (session->init_tstate == session->prev_tstate) { | 
					
						
							|  |  |  |         // We did not switch interpreters.
 | 
					
						
							|  |  |  |         *p_xidata = NULL; | 
					
						
							|  |  |  |         *p_obj = session->_preserved; | 
					
						
							|  |  |  |         session->_preserved = NULL; | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     *p_obj = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // We did switch interpreters.
 | 
					
						
							|  |  |  |     Py_ssize_t len = PyDict_Size(session->_preserved); | 
					
						
							|  |  |  |     if (len < 0) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         failure.code = _PyXI_ERR_PRESERVE_FAILURE; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if (len == 0) { | 
					
						
							|  |  |  |         *p_xidata = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         _PyXI_namespace *xidata = _create_sharedns(session->_preserved); | 
					
						
							|  |  |  |         if (xidata == NULL) { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             failure.code = _PyXI_ERR_PRESERVE_FAILURE; | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (_fill_sharedns(xidata, session->_preserved, | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                            _PyXIDATA_FULL_FALLBACK, &failure) < 0) | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             if (failure.code != _PyXI_ERR_NOT_SHAREABLE) { | 
					
						
							|  |  |  |                 assert(failure.msg != NULL); | 
					
						
							|  |  |  |                 failure.code = _PyXI_ERR_PRESERVE_FAILURE; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |         *p_xidata = xidata; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Py_CLEAR(session->_preserved); | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     if (p_failure != NULL) { | 
					
						
							|  |  |  |         *p_failure = failure; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (xidata != NULL) { | 
					
						
							|  |  |  |         _destroy_sharedns(xidata); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _finish_preserved(_PyXI_namespace *xidata, PyObject **p_preserved) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (xidata == NULL) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     int res = -1; | 
					
						
							|  |  |  |     if (p_preserved != NULL) { | 
					
						
							|  |  |  |         PyObject *ns = PyDict_New(); | 
					
						
							|  |  |  |         if (ns == NULL) { | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (_apply_sharedns(xidata, ns, NULL) < 0) { | 
					
						
							|  |  |  |             Py_CLEAR(ns); | 
					
						
							|  |  |  |             goto finally; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         *p_preserved = ns; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | finally: | 
					
						
							|  |  |  |     _destroy_sharedns(xidata); | 
					
						
							|  |  |  |     return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _PyXI_Preserve(_PyXI_session *session, const char *name, PyObject *value, | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |                _PyXI_failure *p_failure) | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |     _PyXI_failure failure = XI_FAILURE_INIT; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     if (!_session_is_active(session)) { | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_RuntimeError, "session not active"); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (session->_preserved == NULL) { | 
					
						
							|  |  |  |         session->_preserved = PyDict_New(); | 
					
						
							|  |  |  |         if (session->_preserved == NULL) { | 
					
						
							|  |  |  |             set_exc_with_cause(PyExc_RuntimeError, | 
					
						
							|  |  |  |                                "failed to initialize preserved objects"); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |             failure.code = _PyXI_ERR_PRESERVE_FAILURE; | 
					
						
							|  |  |  |             goto error; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (PyDict_SetItemString(session->_preserved, name, value) < 0) { | 
					
						
							|  |  |  |         set_exc_with_cause(PyExc_RuntimeError, "failed to preserve object"); | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  |         failure.code = _PyXI_ERR_PRESERVE_FAILURE; | 
					
						
							|  |  |  |         goto error; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2025-06-13 16:45:21 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | error: | 
					
						
							|  |  |  |     if (p_failure != NULL) { | 
					
						
							|  |  |  |         *p_failure = failure; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return -1; | 
					
						
							| 
									
										
										
										
											2025-05-30 09:15:00 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyObject * | 
					
						
							|  |  |  | _PyXI_GetPreserved(_PyXI_session_result *result, const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyObject *value = NULL; | 
					
						
							|  |  |  |     if (result->preserved != NULL) { | 
					
						
							|  |  |  |         (void)PyDict_GetItemStringRef(result->preserved, name, &value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_ClearResult(_PyXI_session_result *result) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Py_CLEAR(result->preserved); | 
					
						
							|  |  |  |     Py_CLEAR(result->excinfo); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | /*********************/ | 
					
						
							|  |  |  | /* runtime lifecycle */ | 
					
						
							|  |  |  | /*********************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | int | 
					
						
							|  |  |  | _Py_xi_global_state_init(_PyXI_global_state_t *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(state != NULL); | 
					
						
							|  |  |  |     xid_lookup_init(&state->data_lookup); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _Py_xi_global_state_fini(_PyXI_global_state_t *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(state != NULL); | 
					
						
							|  |  |  |     xid_lookup_fini(&state->data_lookup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | _Py_xi_state_init(_PyXI_state_t *state, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(state != NULL); | 
					
						
							|  |  |  |     assert(interp == NULL || state == _PyXI_GET_STATE(interp)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     xid_lookup_init(&state->data_lookup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Initialize exceptions.
 | 
					
						
							|  |  |  |     if (interp != NULL) { | 
					
						
							|  |  |  |         if (init_static_exctypes(&state->exceptions, interp) < 0) { | 
					
						
							|  |  |  |             fini_heap_exctypes(&state->exceptions); | 
					
						
							|  |  |  |             return -1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (init_heap_exctypes(&state->exceptions) < 0) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _Py_xi_state_fini(_PyXI_state_t *state, PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(state != NULL); | 
					
						
							|  |  |  |     assert(interp == NULL || state == _PyXI_GET_STATE(interp)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fini_heap_exctypes(&state->exceptions); | 
					
						
							|  |  |  |     if (interp != NULL) { | 
					
						
							|  |  |  |         fini_static_exctypes(&state->exceptions, interp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     xid_lookup_fini(&state->data_lookup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | PyStatus | 
					
						
							|  |  |  | _PyXI_Init(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     if (_Py_IsMainInterpreter(interp)) { | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); | 
					
						
							|  |  |  |         if (global_state == NULL) { | 
					
						
							|  |  |  |             PyErr_PrintEx(0); | 
					
						
							|  |  |  |             return _PyStatus_ERR( | 
					
						
							|  |  |  |                     "failed to get global cross-interpreter state"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (_Py_xi_global_state_init(global_state) < 0) { | 
					
						
							|  |  |  |             PyErr_PrintEx(0); | 
					
						
							|  |  |  |             return _PyStatus_ERR( | 
					
						
							|  |  |  |                     "failed to initialize  global cross-interpreter state"); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |     _PyXI_state_t *state = _PyXI_GET_STATE(interp); | 
					
						
							|  |  |  |     if (state == NULL) { | 
					
						
							|  |  |  |         PyErr_PrintEx(0); | 
					
						
							|  |  |  |         return _PyStatus_ERR( | 
					
						
							|  |  |  |                 "failed to get interpreter's cross-interpreter state"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // The static types were already initialized in _PyXI_InitTypes(),
 | 
					
						
							|  |  |  |     // so we pass in NULL here to avoid initializing them again.
 | 
					
						
							|  |  |  |     if (_Py_xi_state_init(state, NULL) < 0) { | 
					
						
							| 
									
										
										
										
											2024-11-11 14:49:41 -07:00
										 |  |  |         PyErr_PrintEx(0); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         return _PyStatus_ERR( | 
					
						
							|  |  |  |                 "failed to initialize interpreter's cross-interpreter state"); | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return _PyStatus_OK(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // _PyXI_Fini() must be called before the interpreter is cleared,
 | 
					
						
							|  |  |  | // since we must clear some heap objects.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_Fini(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-11 15:58:46 -07:00
										 |  |  |     _PyXI_state_t *state = _PyXI_GET_STATE(interp); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     if (state == NULL) { | 
					
						
							|  |  |  |         PyErr_PrintEx(0); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     // The static types will be finalized soon in _PyXI_FiniTypes(),
 | 
					
						
							|  |  |  |     // so we pass in NULL here to avoid finalizing them right now.
 | 
					
						
							|  |  |  |     _Py_xi_state_fini(state, NULL); | 
					
						
							| 
									
										
										
										
											2024-11-11 15:58:46 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     if (_Py_IsMainInterpreter(interp)) { | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         _PyXI_global_state_t *global_state = _PyXI_GET_GLOBAL_STATE(interp); | 
					
						
							|  |  |  |         _Py_xi_global_state_fini(global_state); | 
					
						
							| 
									
										
										
										
											2024-11-07 09:32:42 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-01 17:36:40 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | PyStatus | 
					
						
							|  |  |  | _PyXI_InitTypes(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 10:43:50 -06:00
										 |  |  |     if (init_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp) < 0) { | 
					
						
							| 
									
										
										
										
											2024-04-03 10:58:39 -06:00
										 |  |  |         PyErr_PrintEx(0); | 
					
						
							| 
									
										
										
										
											2024-11-12 10:41:51 -07:00
										 |  |  |         return _PyStatus_ERR( | 
					
						
							|  |  |  |                 "failed to initialize the cross-interpreter exception types"); | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-11-11 14:49:41 -07:00
										 |  |  |     // We would initialize heap types here too but that leads to ref leaks.
 | 
					
						
							|  |  |  |     // Instead, we intialize them in _PyXI_Init().
 | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  |     return _PyStatus_OK(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_FiniTypes(PyInterpreterState *interp) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2025-04-25 10:43:50 -06:00
										 |  |  |     // We would finalize heap types here too but that leads to ref leaks.
 | 
					
						
							|  |  |  |     // Instead, we finalize them in _PyXI_Fini().
 | 
					
						
							|  |  |  |     fini_static_exctypes(&_PyXI_GET_STATE(interp)->exceptions, interp); | 
					
						
							| 
									
										
										
										
											2023-12-12 08:24:31 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*************/ | 
					
						
							|  |  |  | /* other API */ | 
					
						
							|  |  |  | /*************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PyInterpreterState * | 
					
						
							| 
									
										
										
										
											2024-04-11 17:23:25 -06:00
										 |  |  | _PyXI_NewInterpreter(PyInterpreterConfig *config, long *maybe_whence, | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |                      PyThreadState **p_tstate, PyThreadState **p_save_tstate) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     PyThreadState *save_tstate = PyThreadState_Swap(NULL); | 
					
						
							|  |  |  |     assert(save_tstate != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PyThreadState *tstate; | 
					
						
							|  |  |  |     PyStatus status = Py_NewInterpreterFromConfig(&tstate, config); | 
					
						
							|  |  |  |     if (PyStatus_Exception(status)) { | 
					
						
							|  |  |  |         // Since no new thread state was created, there is no exception
 | 
					
						
							|  |  |  |         // to propagate; raise a fresh one after swapping back in the
 | 
					
						
							|  |  |  |         // old thread state.
 | 
					
						
							|  |  |  |         PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  |         _PyErr_SetFromPyStatus(status); | 
					
						
							|  |  |  |         PyObject *exc = PyErr_GetRaisedException(); | 
					
						
							|  |  |  |         PyErr_SetString(PyExc_InterpreterError, | 
					
						
							|  |  |  |                         "sub-interpreter creation failed"); | 
					
						
							|  |  |  |         _PyErr_ChainExceptions1(exc); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(tstate != NULL); | 
					
						
							|  |  |  |     PyInterpreterState *interp = PyThreadState_GetInterpreter(tstate); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 17:23:25 -06:00
										 |  |  |     long whence = _PyInterpreterState_WHENCE_XI; | 
					
						
							|  |  |  |     if (maybe_whence != NULL) { | 
					
						
							|  |  |  |         whence = *maybe_whence; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     _PyInterpreterState_SetWhence(interp, whence); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (p_tstate != NULL) { | 
					
						
							|  |  |  |         // We leave the new thread state as the current one.
 | 
					
						
							|  |  |  |         *p_tstate = tstate; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         // Throw away the initial tstate.
 | 
					
						
							|  |  |  |         PyThreadState_Clear(tstate); | 
					
						
							|  |  |  |         PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  |         PyThreadState_Delete(tstate); | 
					
						
							|  |  |  |         save_tstate = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (p_save_tstate != NULL) { | 
					
						
							|  |  |  |         *p_save_tstate = save_tstate; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return interp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | _PyXI_EndInterpreter(PyInterpreterState *interp, | 
					
						
							|  |  |  |                      PyThreadState *tstate, PyThreadState **p_save_tstate) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-04-11 17:23:25 -06:00
										 |  |  | #ifndef NDEBUG
 | 
					
						
							|  |  |  |     long whence = _PyInterpreterState_GetWhence(interp); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     assert(whence != _PyInterpreterState_WHENCE_RUNTIME); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!_PyInterpreterState_IsReady(interp)) { | 
					
						
							|  |  |  |         assert(whence == _PyInterpreterState_WHENCE_UNKNOWN); | 
					
						
							|  |  |  |         // PyInterpreterState_Clear() requires the GIL,
 | 
					
						
							|  |  |  |         // which a not-ready does not have, so we don't clear it.
 | 
					
						
							|  |  |  |         // That means there may be leaks here until clearing the
 | 
					
						
							|  |  |  |         // interpreter is fixed.
 | 
					
						
							|  |  |  |         PyInterpreterState_Delete(interp); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     assert(whence != _PyInterpreterState_WHENCE_UNKNOWN); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |     PyThreadState *save_tstate = NULL; | 
					
						
							|  |  |  |     PyThreadState *cur_tstate = PyThreadState_GET(); | 
					
						
							|  |  |  |     if (tstate == NULL) { | 
					
						
							|  |  |  |         if (PyThreadState_GetInterpreter(cur_tstate) == interp) { | 
					
						
							|  |  |  |             tstate = cur_tstate; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2024-06-25 14:35:12 -06:00
										 |  |  |             tstate = _PyThreadState_NewBound(interp, _PyThreadState_WHENCE_FINI); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  |             assert(tstate != NULL); | 
					
						
							|  |  |  |             save_tstate = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         assert(PyThreadState_GetInterpreter(tstate) == interp); | 
					
						
							|  |  |  |         if (tstate != cur_tstate) { | 
					
						
							|  |  |  |             assert(PyThreadState_GetInterpreter(cur_tstate) != interp); | 
					
						
							|  |  |  |             save_tstate = PyThreadState_Swap(tstate); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 17:23:25 -06:00
										 |  |  |     Py_EndInterpreter(tstate); | 
					
						
							| 
									
										
										
										
											2024-04-10 18:37:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (p_save_tstate != NULL) { | 
					
						
							|  |  |  |         save_tstate = *p_save_tstate; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     PyThreadState_Swap(save_tstate); | 
					
						
							|  |  |  | } |