Commit 3078039d authored by Brett Cannon's avatar Brett Cannon

Issue #15316: Let exceptions raised during imports triggered by the

fromlist of __import__ propagate.

The problem previously was that if something listed in fromlist didn't
exist then that's okay. The fix for that was too broad in terms of
catching ImportError.

The trick with the solution to this issue is that the proper
refactoring of import thanks to importlib doesn't allow for a way to
distinguish (portably) between an ImportError because finders couldn't
find a loader, or a loader raised the exception. In Python 3.4 the
hope is to introduce a new exception (e.g. ModuleNotFound) to make it
clean to differentiate why ImportError was raised.
parent 11d98122
......@@ -1513,7 +1513,11 @@ def _find_and_load_unlocked(name, import_):
raise ImportError(msg, name=name)
loader = _find_module(name, path)
if loader is None:
raise ImportError(_ERR_MSG.format(name), name=name)
exc = ImportError(_ERR_MSG.format(name), name=name)
# TODO(brett): switch to a proper ModuleNotFound exception in Python
# 3.4.
exc._not_found = True
raise exc
elif name not in sys.modules:
# The parent import may have already imported this module.
loader.load_module(name)
......@@ -1599,10 +1603,16 @@ def _handle_fromlist(module, fromlist, import_):
try:
_call_with_frames_removed(import_,
'{}.{}'.format(module.__name__, x))
except ImportError:
except ImportError as exc:
# Backwards-compatibility dictates we ignore failed
# imports triggered by fromlist.
pass
# imports triggered by fromlist for modules that don't
# exist.
# TODO(brett): In Python 3.4, have import raise
# ModuleNotFound and catch that.
if hasattr(exc, '_not_found') and exc._not_found:
pass
else:
raise
return module
......
from .. import util as importlib_test_util
from . import util
import imp
import sys
import unittest
class BadLoaderFinder:
bad = 'fine.bogus'
@classmethod
def find_module(cls, fullname, path):
if fullname == cls.bad:
return cls
@classmethod
def load_module(cls, fullname):
if fullname == cls.bad:
raise ImportError('I cannot be loaded!')
class APITest(unittest.TestCase):
"""Test API-specific details for __import__ (e.g. raising the right
......@@ -19,6 +34,29 @@ class APITest(unittest.TestCase):
with self.assertRaises(ValueError):
util.import_('os', globals(), level=-1)
def test_nonexistent_fromlist_entry(self):
# If something in fromlist doesn't exist, that's okay.
# issue15715
mod = imp.new_module('fine')
mod.__path__ = ['XXX']
with importlib_test_util.import_state(meta_path=[BadLoaderFinder]):
with importlib_test_util.uncache('fine'):
sys.modules['fine'] = mod
util.import_('fine', fromlist=['not here'])
def test_fromlist_load_error_propagates(self):
# If something in fromlist triggers an exception not related to not
# existing, let that exception propagate.
# issue15316
mod = imp.new_module('fine')
mod.__path__ = ['XXX']
with importlib_test_util.import_state(meta_path=[BadLoaderFinder]):
with importlib_test_util.uncache('fine'):
sys.modules['fine'] = mod
with self.assertRaises(ImportError):
util.import_('fine', fromlist=['bogus'])
def test_main():
from test.support import run_unittest
......
......@@ -10,6 +10,10 @@ What's New in Python 3.3.0 Release Candidate 1?
Core and Builtins
-----------------
- Issue #15316: When an item in the fromlist for __import__ doesn't exist,
don't raise an error, but if an exception is raised as part of an import do
let that propagate.
- Issue #15778: ensure that str(ImportError(msg)) returns a str
even when msg isn't a str.
......
This diff is collapsed.
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