Kaydet (Commit) b4baacee authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka Kaydeden (comit) GitHub

bpo-30814: Fixed a race condition when import a submodule from a package. (#2580)

üst 1ccbad9c
...@@ -956,9 +956,19 @@ def _find_and_load_unlocked(name, import_): ...@@ -956,9 +956,19 @@ def _find_and_load_unlocked(name, import_):
def _find_and_load(name, import_): def _find_and_load(name, import_):
"""Find and load the module, and release the import lock.""" """Find and load the module."""
_imp.acquire_lock()
if name not in sys.modules:
with _ModuleLockManager(name): with _ModuleLockManager(name):
return _find_and_load_unlocked(name, import_) return _find_and_load_unlocked(name, 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
def _gcd_import(name, package=None, level=0): def _gcd_import(name, package=None, level=0):
...@@ -973,17 +983,7 @@ 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) _sanity_check(name, package, level)
if level > 0: if level > 0:
name = _resolve_name(name, package, level) name = _resolve_name(name, package, level)
_imp.acquire_lock()
if name not in sys.modules:
return _find_and_load(name, _gcd_import) 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
def _handle_fromlist(module, fromlist, import_): def _handle_fromlist(module, fromlist, import_):
......
...@@ -10,6 +10,8 @@ import py_compile ...@@ -10,6 +10,8 @@ import py_compile
import random import random
import stat import stat
import sys import sys
import threading
import time
import unittest import unittest
import unittest.mock as mock import unittest.mock as mock
import textwrap import textwrap
...@@ -380,6 +382,32 @@ class ImportTests(unittest.TestCase): ...@@ -380,6 +382,32 @@ class ImportTests(unittest.TestCase):
self.assertEqual(str(cm.exception), self.assertEqual(str(cm.exception),
"cannot import name 'does_not_exist' from '<unknown module name>' (unknown location)") "cannot import name 'does_not_exist' from '<unknown module name>' (unknown location)")
def test_concurrency(self):
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'data'))
try:
exc = None
def run():
event.wait()
try:
import package
except BaseException as e:
nonlocal exc
exc = e
for i in range(10):
event = threading.Event()
threads = [threading.Thread(target=run) for x in range(2)]
try:
with test.support.start_threads(threads, event.set):
time.sleep(0)
finally:
sys.modules.pop('package', None)
sys.modules.pop('package.submodule', None)
if exc is not None:
raise exc
finally:
del sys.path[0]
@skip_if_dont_write_bytecode @skip_if_dont_write_bytecode
class FilePermissionTests(unittest.TestCase): class FilePermissionTests(unittest.TestCase):
......
import package.submodule
package.submodule
...@@ -10,6 +10,8 @@ What's New in Python 3.7.0 alpha 1? ...@@ -10,6 +10,8 @@ What's New in Python 3.7.0 alpha 1?
Core and Builtins Core and Builtins
----------------- -----------------
- bpo-30814: Fixed a race condition when import a submodule from a package.
- bpo-30736: The internal unicodedata database has been upgraded to Unicode - bpo-30736: The internal unicodedata database has been upgraded to Unicode
10.0. 10.0.
......
...@@ -1532,18 +1532,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, ...@@ -1532,18 +1532,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
} }
mod = PyDict_GetItem(interp->modules, abs_name); mod = PyDict_GetItem(interp->modules, abs_name);
if (mod == Py_None) { if (mod != NULL && 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) {
_Py_IDENTIFIER(__spec__); _Py_IDENTIFIER(__spec__);
_Py_IDENTIFIER(_initializing); _Py_IDENTIFIER(_initializing);
_Py_IDENTIFIER(_lock_unlock_module); _Py_IDENTIFIER(_lock_unlock_module);
...@@ -1584,10 +1573,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, ...@@ -1584,10 +1573,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
} }
} }
else { else {
#ifdef WITH_THREAD
_PyImport_AcquireLock();
#endif
/* _bootstrap._find_and_load() releases the import lock */
mod = _PyObject_CallMethodIdObjArgs(interp->importlib, mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
&PyId__find_and_load, abs_name, &PyId__find_and_load, abs_name,
interp->import_func, NULL); interp->import_func, NULL);
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment