diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2017-07-06 05:09:03 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-06 05:09:03 (GMT) |
commit | b4baacee1adc06edbe30ac7574d17a8cd168e2e0 (patch) | |
tree | a01eb344f832a876239e6a7dd33806f5945206c2 /Lib | |
parent | 1ccbad9c95cf5782a1329eeaecba6e3eb0c37cb8 (diff) | |
download | cpython-b4baacee1adc06edbe30ac7574d17a8cd168e2e0.zip cpython-b4baacee1adc06edbe30ac7574d17a8cd168e2e0.tar.gz cpython-b4baacee1adc06edbe30ac7574d17a8cd168e2e0.tar.bz2 |
bpo-30814: Fixed a race condition when import a submodule from a package. (#2580)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/importlib/_bootstrap.py | 28 | ||||
-rw-r--r-- | Lib/test/test_import/__init__.py | 28 | ||||
-rw-r--r-- | Lib/test/test_import/data/package/__init__.py | 2 | ||||
-rw-r--r-- | Lib/test/test_import/data/package/submodule.py | 0 |
4 files changed, 44 insertions, 14 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 891bd06..f7c89ca 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -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; ' + '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_): diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index a78fae1..8424ed7 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -10,6 +10,8 @@ import py_compile import random import stat import sys +import threading +import time import unittest import unittest.mock as mock import textwrap @@ -380,6 +382,32 @@ class ImportTests(unittest.TestCase): self.assertEqual(str(cm.exception), "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 class FilePermissionTests(unittest.TestCase): diff --git a/Lib/test/test_import/data/package/__init__.py b/Lib/test/test_import/data/package/__init__.py new file mode 100644 index 0000000..a4f2bc3 --- /dev/null +++ b/Lib/test/test_import/data/package/__init__.py @@ -0,0 +1,2 @@ +import package.submodule +package.submodule diff --git a/Lib/test/test_import/data/package/submodule.py b/Lib/test/test_import/data/package/submodule.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Lib/test/test_import/data/package/submodule.py |