(original) (raw)
changeset: 76826:d6324941b739 user: Antoine Pitrou solipsis@pitrou.net date: Mon May 07 21:41:59 2012 +0200 files: Lib/importlib/_bootstrap.py Lib/importlib/test/import_/test_packages.py Lib/importlib/test/util.py Misc/NEWS Python/import.c Python/importlib.h description: Issue #14583: Fix importlib bug when a package's __init__.py would first import one of its modules then raise an error. diff -r d50577c5711b -r d6324941b739 Lib/importlib/_bootstrap.py --- a/Lib/importlib/_bootstrap.py Mon May 07 17:25:14 2012 +0100 +++ b/Lib/importlib/_bootstrap.py Mon May 07 21:41:59 2012 +0200 @@ -1082,7 +1082,7 @@ # Return up to the first dot in 'name'. This is complicated by the fact # that 'name' may be relative. if level == 0: - return sys.modules[name.partition('.')[0]] + return _gcd_import(name.partition('.')[0]) elif not name: return module else: diff -r d50577c5711b -r d6324941b739 Lib/importlib/test/import_/test_packages.py --- a/Lib/importlib/test/import_/test_packages.py Mon May 07 17:25:14 2012 +0100 +++ b/Lib/importlib/test/import_/test_packages.py Mon May 07 21:41:59 2012 +0200 @@ -23,6 +23,62 @@ import_util.import_('pkg.module') 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): # Try to import a submodule from a non-package should raise ImportError. assert not hasattr(sys, '__path__') diff -r d50577c5711b -r d6324941b739 Lib/importlib/test/util.py --- a/Lib/importlib/test/util.py Mon May 07 17:25:14 2012 +0100 +++ b/Lib/importlib/test/util.py Mon May 07 21:41:59 2012 +0200 @@ -124,7 +124,11 @@ else: sys.modules[fullname] = self.modules[fullname] 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] def __enter__(self): diff -r d50577c5711b -r d6324941b739 Misc/NEWS --- a/Misc/NEWS Mon May 07 17:25:14 2012 +0100 +++ b/Misc/NEWS Mon May 07 21:41:59 2012 +0200 @@ -20,6 +20,9 @@ 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 #14697: Fix missing support for set displays and set comprehensions in diff -r d50577c5711b -r d6324941b739 Python/import.c --- a/Python/import.c Mon May 07 17:25:14 2012 +0100 +++ b/Python/import.c Mon May 07 21:41:59 2012 +0200 @@ -1633,19 +1633,20 @@ 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); Py_INCREF(front); Py_DECREF(partition); if (level == 0) { - final_mod = PyDict_GetItem(interp->modules, front); - if (final_mod == NULL) { - PyErr_Format(PyExc_KeyError, - "%R not in sys.modules as expected", front); - } - else { - Py_INCREF(final_mod); - } + final_mod = PyObject_CallFunctionObjArgs(builtins_import, front, NULL); Py_DECREF(front); } else { @@ -1682,6 +1683,8 @@ fromlist, builtins_import, NULL); } + + exit_with_unlock: error_with_unlock: #ifdef WITH_THREAD if (_PyImport_ReleaseLock() < 0) { diff -r d50577c5711b -r d6324941b739 Python/importlib.h Binary file Python/importlib.h has changed /solipsis@pitrou.net