-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-30814: Fixed a race condition when import a submodule from a package. #2580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -956,9 +956,19 @@ def _find_and_load_unlocked(name, import_): | |
|
||
|
||
def _find_and_load(name, import_): | ||
"""Find and load the module, and release the import lock.""" | ||
with _ModuleLockManager(name): | ||
return _find_and_load_unlocked(name, import_) | ||
"""Find and load the module.""" | ||
_imp.acquire_lock() | ||
if name not in sys.modules: | ||
with _ModuleLockManager(name): | ||
return _find_and_load_unlocked(name, import_) | ||
module = sys.modules[name] | ||
if module is None: | ||
_imp.release_lock() | ||
message = ('import of {} halted; ' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would call |
||
'None in sys.modules'.format(name)) | ||
raise ModuleNotFoundError(message, name=name) | ||
_lock_unlock_module(name) | ||
return module | ||
|
||
|
||
def _gcd_import(name, package=None, level=0): | ||
|
@@ -973,17 +983,7 @@ def _gcd_import(name, package=None, level=0): | |
_sanity_check(name, package, level) | ||
if level > 0: | ||
name = _resolve_name(name, package, level) | ||
_imp.acquire_lock() | ||
if name not in sys.modules: | ||
return _find_and_load(name, _gcd_import) | ||
module = sys.modules[name] | ||
if module is None: | ||
_imp.release_lock() | ||
message = ('import of {} halted; ' | ||
'None in sys.modules'.format(name)) | ||
raise ModuleNotFoundError(message, name=name) | ||
_lock_unlock_module(name) | ||
return module | ||
return _find_and_load(name, _gcd_import) | ||
|
||
|
||
def _handle_fromlist(module, fromlist, import_): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import package.submodule | ||
package.submodule |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1532,18 +1532,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, | |
} | ||
|
||
mod = PyDict_GetItem(interp->modules, abs_name); | ||
if (mod == Py_None) { | ||
PyObject *msg = PyUnicode_FromFormat("import of %R halted; " | ||
"None in sys.modules", abs_name); | ||
if (msg != NULL) { | ||
PyErr_SetImportErrorSubclass(PyExc_ModuleNotFoundError, msg, | ||
abs_name, NULL); | ||
Py_DECREF(msg); | ||
} | ||
mod = NULL; | ||
goto error; | ||
} | ||
else if (mod != NULL) { | ||
if (mod != NULL && mod != Py_None) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the separate helper function, we'd keep the dedicated |
||
_Py_IDENTIFIER(__spec__); | ||
_Py_IDENTIFIER(_initializing); | ||
_Py_IDENTIFIER(_lock_unlock_module); | ||
|
@@ -1584,10 +1573,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, | |
} | ||
} | ||
else { | ||
#ifdef WITH_THREAD | ||
_PyImport_AcquireLock(); | ||
#endif | ||
/* _bootstrap._find_and_load() releases the import lock */ | ||
mod = _PyObject_CallMethodIdObjArgs(interp->importlib, | ||
&PyId__find_and_load, abs_name, | ||
interp->import_func, NULL); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This consolidation seems a little confusing to me, but I like the idea of simplifying the C code by moving the
ModuleNotFoundError
generation into Python.How about adding a dedicated
_raise_for_halted_import(name, import_):
function that just handles themodule is None
case?