diff --git a/Lib/imp.py b/Lib/imp.py index 6d156d3115c..40869f5ac06 100644 --- a/Lib/imp.py +++ b/Lib/imp.py @@ -168,7 +168,7 @@ def load_module(name, file, filename, details): warnings.simplefilter('ignore') if mode and (not mode.startswith(('r', 'U')) or '+' in mode): raise ValueError('invalid file open mode {!r}'.format(mode)) - elif file is None and type_ in {PY_SOURCE, PY_COMPILED, C_EXTENSION}: + elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: msg = 'file object required for import (type code {})'.format(type_) raise ValueError(msg) elif type_ == PY_SOURCE: @@ -176,7 +176,11 @@ def load_module(name, file, filename, details): elif type_ == PY_COMPILED: return load_compiled(name, filename, file) elif type_ == C_EXTENSION and load_dynamic is not None: - return load_dynamic(name, filename, file) + if file is None: + with open(filename, 'rb') as opened_file: + return load_dynamic(name, filename, opened_file) + else: + return load_dynamic(name, filename, file) elif type_ == PKG_DIRECTORY: return load_package(name, filename) elif type_ == C_BUILTIN: diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py index 2d757247718..74a7b593f98 100644 --- a/Lib/test/test_imp.py +++ b/Lib/test/test_imp.py @@ -208,6 +208,8 @@ class ImportTests(unittest.TestCase): self.assertIsNot(orig_getenv, new_os.getenv) @support.cpython_only + @unittest.skipIf(not hasattr(imp, 'load_dynamic'), + 'imp.load_dynamic() required') def test_issue15828_load_extensions(self): # Issue 15828 picked up that the adapter between the old imp API # and importlib couldn't handle C extensions @@ -244,6 +246,19 @@ class ImportTests(unittest.TestCase): self.assertIn(path, err.exception.path) self.assertEqual(name, err.exception.name) + @support.cpython_only + @unittest.skipIf(not hasattr(imp, 'load_dynamic'), + 'imp.load_dynamic() required') + def test_load_module_extension_file_is_None(self): + # When loading an extension module and the file is None, open one + # on the behalf of imp.load_dynamic(). + # Issue #15902 + name = '_heapq' + found = imp.find_module(name) + assert found[2][2] == imp.C_EXTENSION + found[0].close() + imp.load_module(name, None, *found[1:]) + class ReloadTests(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS index bc98c0d85bc..5682eceacf9 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -60,6 +60,9 @@ Core and Builtins Library ------- + - Issue #15902: Fix imp.load_module() accepting None as a file when loading an + extension module. + - Issue #13721: SSLSocket.getpeercert() and SSLSocket.do_handshake() now raise an OSError with ENOTCONN, instead of an AttributeError, when the SSLSocket is not connected.