summaryrefslogtreecommitdiffstats
path: root/Python/import.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/import.c')
-rw-r--r--Python/import.c18
1 files changed, 13 insertions, 5 deletions
diff --git a/Python/import.c b/Python/import.c
index 26ad843..460b1fe 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -815,6 +815,8 @@ static int clear_singlephase_extension(PyInterpreterState *interp,
// Currently, this is only used for testing.
// (See _testinternalcapi.clear_extension().)
+// If adding another use, be careful about modules that import themselves
+// recursively (see gh-123880).
int
_PyImport_ClearExtension(PyObject *name, PyObject *filename)
{
@@ -1322,12 +1324,16 @@ _extensions_cache_set(PyObject *path, PyObject *name,
value = entry == NULL
? NULL
: (struct extensions_cache_value *)entry->value;
- /* We should never be updating an existing cache value. */
- assert(value == NULL);
if (value != NULL) {
- PyErr_Format(PyExc_SystemError,
- "extension module %R is already cached", name);
- goto finally;
+ /* gh-123880: If there's an existing cache value, it means a module is
+ * being imported recursively from its PyInit_* or Py_mod_* function.
+ * (That function presumably handles returning a partially
+ * constructed module in such a case.)
+ * We can reuse the existing cache value; it is owned by the cache.
+ * (Entries get removed from it in exceptional circumstances,
+ * after interpreter shutdown, and in runtime shutdown.)
+ */
+ goto finally_oldvalue;
}
newvalue = alloc_extensions_cache_value();
if (newvalue == NULL) {
@@ -1392,6 +1398,7 @@ finally:
cleanup_old_cached_def(&olddefbase);
}
+finally_oldvalue:
extensions_lock_release();
if (key != NULL) {
hashtable_destroy_str(key);
@@ -2128,6 +2135,7 @@ error:
}
+// Used in _PyImport_ClearExtension; see notes there.
static int
clear_singlephase_extension(PyInterpreterState *interp,
PyObject *name, PyObject *path)