Commit 87a0e2dd authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #14583: Fix importlib bug when a package's __init__.py would first...

Issue #14583: Fix importlib bug when a package's __init__.py would first import one of its modules then raise an error.
parent 5ae4d308
...@@ -1082,7 +1082,7 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0): ...@@ -1082,7 +1082,7 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0):
# Return up to the first dot in 'name'. This is complicated by the fact # Return up to the first dot in 'name'. This is complicated by the fact
# that 'name' may be relative. # that 'name' may be relative.
if level == 0: if level == 0:
return sys.modules[name.partition('.')[0]] return _gcd_import(name.partition('.')[0])
elif not name: elif not name:
return module return module
else: else:
......
...@@ -23,6 +23,62 @@ class ParentModuleTests(unittest.TestCase): ...@@ -23,6 +23,62 @@ class ParentModuleTests(unittest.TestCase):
import_util.import_('pkg.module') import_util.import_('pkg.module')
self.assertEqual(cm.exception.name, 'pkg') self.assertEqual(cm.exception.name, 'pkg')
def test_raising_parent_after_importing_child(self):
def __init__():
import pkg.module
1/0
mock = util.mock_modules('pkg.__init__', 'pkg.module',
module_code={'pkg': __init__})
with mock:
with util.import_state(meta_path=[mock]):
with self.assertRaises(ZeroDivisionError):
import_util.import_('pkg')
self.assertFalse('pkg' in sys.modules)
self.assertTrue('pkg.module' in sys.modules)
with self.assertRaises(ZeroDivisionError):
import_util.import_('pkg.module')
self.assertFalse('pkg' in sys.modules)
self.assertTrue('pkg.module' in sys.modules)
def test_raising_parent_after_relative_importing_child(self):
def __init__():
from . import module
1/0
mock = util.mock_modules('pkg.__init__', 'pkg.module',
module_code={'pkg': __init__})
with mock:
with util.import_state(meta_path=[mock]):
with self.assertRaises((ZeroDivisionError, ImportError)):
# This raises ImportError on the "from . import module"
# line, not sure why.
import_util.import_('pkg')
self.assertFalse('pkg' in sys.modules)
with self.assertRaises((ZeroDivisionError, ImportError)):
import_util.import_('pkg.module')
self.assertFalse('pkg' in sys.modules)
# XXX False
#self.assertTrue('pkg.module' in sys.modules)
def test_raising_parent_after_double_relative_importing_child(self):
def __init__():
from ..subpkg import module
1/0
mock = util.mock_modules('pkg.__init__', 'pkg.subpkg.__init__',
'pkg.subpkg.module',
module_code={'pkg.subpkg': __init__})
with mock:
with util.import_state(meta_path=[mock]):
with self.assertRaises((ZeroDivisionError, ImportError)):
# This raises ImportError on the "from ..subpkg import module"
# line, not sure why.
import_util.import_('pkg.subpkg')
self.assertFalse('pkg.subpkg' in sys.modules)
with self.assertRaises((ZeroDivisionError, ImportError)):
import_util.import_('pkg.subpkg.module')
self.assertFalse('pkg.subpkg' in sys.modules)
# XXX False
#self.assertTrue('pkg.subpkg.module' in sys.modules)
def test_module_not_package(self): def test_module_not_package(self):
# Try to import a submodule from a non-package should raise ImportError. # Try to import a submodule from a non-package should raise ImportError.
assert not hasattr(sys, '__path__') assert not hasattr(sys, '__path__')
......
...@@ -124,7 +124,11 @@ class mock_modules: ...@@ -124,7 +124,11 @@ class mock_modules:
else: else:
sys.modules[fullname] = self.modules[fullname] sys.modules[fullname] = self.modules[fullname]
if fullname in self.module_code: if fullname in self.module_code:
self.module_code[fullname]() try:
self.module_code[fullname]()
except Exception:
del sys.modules[fullname]
raise
return self.modules[fullname] return self.modules[fullname]
def __enter__(self): def __enter__(self):
......
...@@ -20,6 +20,9 @@ Core and Builtins ...@@ -20,6 +20,9 @@ Core and Builtins
Library Library
------- -------
- Issue #14583: Fix importlib bug when a package's __init__.py would first
import one of its modules then raise an error.
- Issue #14741: Fix missing support for Ellipsis ('...') in parser module. - Issue #14741: Fix missing support for Ellipsis ('...') in parser module.
- Issue #14697: Fix missing support for set displays and set comprehensions in - Issue #14697: Fix missing support for set displays and set comprehensions in
......
...@@ -1633,19 +1633,20 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals, ...@@ -1633,19 +1633,20 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
goto error_with_unlock; goto error_with_unlock;
} }
if (PyUnicode_GET_LENGTH(PyTuple_GET_ITEM(partition, 1)) == 0) {
/* No dot in module name, simple exit */
Py_DECREF(partition);
final_mod = mod;
Py_INCREF(mod);
goto exit_with_unlock;
}
front = PyTuple_GET_ITEM(partition, 0); front = PyTuple_GET_ITEM(partition, 0);
Py_INCREF(front); Py_INCREF(front);
Py_DECREF(partition); Py_DECREF(partition);
if (level == 0) { if (level == 0) {
final_mod = PyDict_GetItem(interp->modules, front); final_mod = PyObject_CallFunctionObjArgs(builtins_import, front, NULL);
if (final_mod == NULL) {
PyErr_Format(PyExc_KeyError,
"%R not in sys.modules as expected", front);
}
else {
Py_INCREF(final_mod);
}
Py_DECREF(front); Py_DECREF(front);
} }
else { else {
...@@ -1682,6 +1683,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals, ...@@ -1682,6 +1683,8 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
fromlist, builtins_import, fromlist, builtins_import,
NULL); NULL);
} }
exit_with_unlock:
error_with_unlock: error_with_unlock:
#ifdef WITH_THREAD #ifdef WITH_THREAD
if (_PyImport_ReleaseLock() < 0) { if (_PyImport_ReleaseLock() < 0) {
......
No preview for this file type
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