diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-18-34-45.gh-issue-154561.vuDLtN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-18-34-45.gh-issue-154561.vuDLtN.rst new file mode 100644 index 00000000000000..08a4a4a2fe5781 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-18-34-45.gh-issue-154561.vuDLtN.rst @@ -0,0 +1,3 @@ +Fix a race condition in the free-threaded build where concurrent access to +the same lazy-imported submodule attribute could raise :exc:`AttributeError`. +Patch by Bartosz Sławecki. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index b8cd6025c20ba5..c24435bdbf0068 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1398,6 +1398,16 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) if (PyErr_Occurred()) { return NULL; } + // Other threads could have raced to load the submodule. + // Re-check the module dict to avoid race condition. + attr = _PyObject_GenericGetAttrWithDict((PyObject *)m, name, NULL, 1); + if (attr != NULL) { + return attr; + } + if (PyErr_Occurred()) { + // pass up non-AttributeError exception + return NULL; + } if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) { return NULL; }