summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2017-07-06 05:09:03 (GMT)
committerGitHub <noreply@github.com>2017-07-06 05:09:03 (GMT)
commitb4baacee1adc06edbe30ac7574d17a8cd168e2e0 (patch)
treea01eb344f832a876239e6a7dd33806f5945206c2 /Lib
parent1ccbad9c95cf5782a1329eeaecba6e3eb0c37cb8 (diff)
downloadcpython-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.py28
-rw-r--r--Lib/test/test_import/__init__.py28
-rw-r--r--Lib/test/test_import/data/package/__init__.py2
-rw-r--r--Lib/test/test_import/data/package/submodule.py0
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