From 6db588c626afa9c678169f41d560b151f5bf01a4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 9 Apr 2025 15:26:16 -0600 Subject: [PATCH 1/2] Add _PyImport_GetModulesRef(). --- Include/internal/pycore_import.h | 1 + Python/import.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 3acafd02bbdd7c..13fbff4eb65cb2 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -63,6 +63,7 @@ extern void _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val); extern PyObject * _PyImport_InitModules(PyInterpreterState *interp); extern PyObject * _PyImport_GetModules(PyInterpreterState *interp); +extern PyObject * _PyImport_GetModulesRef(PyInterpreterState *interp); extern void _PyImport_ClearModules(PyInterpreterState *interp); extern void _PyImport_ClearModulesByIndex(PyInterpreterState *interp); diff --git a/Python/import.c b/Python/import.c index 8742b6ec767c9b..a671a08daeb50f 100644 --- a/Python/import.c +++ b/Python/import.c @@ -153,6 +153,20 @@ _PyImport_GetModules(PyInterpreterState *interp) return MODULES(interp); } +PyObject * +_PyImport_GetModulesRef(PyInterpreterState *interp) +{ + _PyImport_AcquireLock(interp); + PyObject *modules = MODULES(interp); + if (modules == NULL) { + /* The interpreter hasn't been initialized yet. */ + modules = Py_None; + } + Py_INCREF(modules); + _PyImport_ReleaseLock(interp); + return modules; +} + void _PyImport_ClearModules(PyInterpreterState *interp) { From 9c9c41c1547836208d86456df606ee1080586e5c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 9 Apr 2025 15:28:01 -0600 Subject: [PATCH 2/2] Replace PyUnstable_InterpreterState_GetMainModule() with _Py_GetMainModule() and _Py_CheckMainModule(). --- Doc/c-api/init.rst | 10 -------- Include/cpython/pystate.h | 2 -- Include/internal/pycore_pystate.h | 3 +++ Python/crossinterp.c | 5 ++-- Python/pystate.c | 38 ++++++++++++++++++++++++++----- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 3597f35e0a2656..b19bddc728b7cf 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1517,16 +1517,6 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. versionadded:: 3.8 -.. c:function:: PyObject* PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *interp) - - Return a :term:`strong reference` to the ``__main__`` :ref:`module object ` - for the given interpreter. - - The caller must have an :term:`attached thread state`. - - .. versionadded:: 3.13 - - .. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) Type of a frame evaluation function. diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index c562426767c2cb..97f11fff244a8b 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -8,8 +8,6 @@ PyAPI_FUNC(int) _PyInterpreterState_RequiresIDRef(PyInterpreterState *); PyAPI_FUNC(void) _PyInterpreterState_RequireIDRef(PyInterpreterState *, int); -PyAPI_FUNC(PyObject *) PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *); - /* State unique per thread */ /* Py_tracefunc return -1 when raising an exception, or 0 for success. */ diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 864e0f5d1db289..601b9790001a03 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -283,6 +283,9 @@ PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); // See also PyInterpreterState_Get() and _PyInterpreterState_GET(). extern PyInterpreterState* _PyGILState_GetInterpreterStateUnsafe(void); +extern PyObject * _Py_GetMainModule(PyThreadState *); +extern int _Py_CheckMainModule(PyObject *module); + #ifndef NDEBUG /* Modern equivalent of assert(PyGILState_Check()) */ static inline void diff --git a/Python/crossinterp.c b/Python/crossinterp.c index e4368165a35326..0fc118977732ea 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -1728,6 +1728,7 @@ _PyXI_Enter(_PyXI_session *session, // Switch to the requested interpreter (if necessary). _enter_session(session, interp); + PyThreadState *session_tstate = session->init_tstate; _PyXI_errcode errcode = _PyXI_ERR_UNCAUGHT_EXCEPTION; // Ensure this thread owns __main__. @@ -1741,8 +1742,8 @@ _PyXI_Enter(_PyXI_session *session, session->running = 1; // Cache __main__.__dict__. - PyObject *main_mod = PyUnstable_InterpreterState_GetMainModule(interp); - if (main_mod == NULL) { + PyObject *main_mod = _Py_GetMainModule(session_tstate); + if (_Py_CheckMainModule(main_mod) < 0) { errcode = _PyXI_ERR_MAIN_NS_FAILURE; goto error; } diff --git a/Python/pystate.c b/Python/pystate.c index aba558279a657d..ec19e3ae6a415a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1210,14 +1210,40 @@ _PyInterpreterState_SetWhence(PyInterpreterState *interp, long whence) PyObject * -PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *interp) +_Py_GetMainModule(PyThreadState *tstate) { - PyObject *modules = _PyImport_GetModules(interp); - if (modules == NULL) { - PyErr_SetString(PyExc_RuntimeError, "interpreter not initialized"); - return NULL; + // We return None to indicate "not found" or "bogus". + PyObject *modules = _PyImport_GetModulesRef(tstate->interp); + if (modules == Py_None) { + return modules; + } + PyObject *module = NULL; + (void)PyMapping_GetOptionalItem(modules, &_Py_ID(__main__), &module); + Py_DECREF(modules); + if (module == NULL && !PyErr_Occurred()) { + Py_RETURN_NONE; } - return PyMapping_GetItemString(modules, "__main__"); + return module; +} + +int +_Py_CheckMainModule(PyObject *module) +{ + if (module == NULL || module == Py_None) { + if (!PyErr_Occurred()) { + (void)_PyErr_SetModuleNotFoundError(&_Py_ID(__main__)); + } + return -1; + } + if (!Py_IS_TYPE(module, &PyModule_Type)) { + /* The __main__ module has been tampered with. */ + PyObject *msg = PyUnicode_FromString("invalid __main__ module"); + if (msg != NULL) { + (void)PyErr_SetImportError(msg, &_Py_ID(__main__), NULL); + } + return -1; + } + return 0; }