summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/importlib/_bootstrap.py25
-rw-r--r--Lib/test/test_import.py93
2 files changed, 115 insertions, 3 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 97ab18d..0666168 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -541,7 +541,7 @@ class FrozenImporter:
"""Load a frozen module."""
is_reload = fullname in sys.modules
try:
- m = _imp.init_frozen(fullname)
+ m = cls._exec_module(fullname)
# Let our own module_repr() method produce a suitable repr.
del m.__file__
return m
@@ -568,6 +568,13 @@ class FrozenImporter:
"""Return if the frozen module is a package."""
return _imp.is_frozen_package(fullname)
+ @classmethod
+ def _exec_module(cls, fullname):
+ """Helper for load_module, allowing to isolate easily (when
+ looking at a traceback) whether an error comes from executing
+ an imported module's code."""
+ return _imp.init_frozen(fullname)
+
class _LoaderBasics:
@@ -644,9 +651,15 @@ class _LoaderBasics:
else:
module.__package__ = module.__package__.rpartition('.')[0]
module.__loader__ = self
- exec(code_object, module.__dict__)
+ self._exec_module(code_object, module.__dict__)
return module
+ def _exec_module(self, code_object, module_dict):
+ """Helper for _load_module, allowing to isolate easily (when
+ looking at a traceback) whether an error comes from executing
+ an imported module's code."""
+ exec(code_object, module_dict)
+
class SourceLoader(_LoaderBasics):
@@ -869,7 +882,7 @@ class ExtensionFileLoader:
"""Load an extension module."""
is_reload = fullname in sys.modules
try:
- module = _imp.load_dynamic(fullname, self.path)
+ module = self._exec_module(fullname, self.path)
_verbose_message('extension module loaded from {!r}', self.path)
return module
except:
@@ -889,6 +902,12 @@ class ExtensionFileLoader:
"""Return None as extension modules have no source code."""
return None
+ def _exec_module(self, fullname, path):
+ """Helper for load_module, allowing to isolate easily (when
+ looking at a traceback) whether an error comes from executing
+ an imported module's code."""
+ return _imp.load_dynamic(fullname, path)
+
class _NamespacePath:
"""Represents a namespace package's path. It uses the module name
diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py
index 1746a61..35a4baf 100644
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -768,6 +768,98 @@ class ImportlibBootstrapTests(unittest.TestCase):
self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__)
+class ImportTracebackTests(unittest.TestCase):
+
+ def setUp(self):
+ os.mkdir(TESTFN)
+ self.old_path = sys.path[:]
+ sys.path.insert(0, TESTFN)
+
+ def tearDown(self):
+ sys.path[:] = self.old_path
+ rmtree(TESTFN)
+
+ def create_module(self, mod, contents):
+ with open(os.path.join(TESTFN, mod + ".py"), "w") as f:
+ f.write(contents)
+ self.addCleanup(unload, mod)
+ importlib.invalidate_caches()
+
+ def assert_traceback(self, tb, files):
+ deduped_files = []
+ while tb:
+ code = tb.tb_frame.f_code
+ fn = code.co_filename
+ if not deduped_files or fn != deduped_files[-1]:
+ deduped_files.append(fn)
+ tb = tb.tb_next
+ self.assertEqual(len(deduped_files), len(files), deduped_files)
+ for fn, pat in zip(deduped_files, files):
+ self.assertRegex(fn, pat)
+
+ def test_nonexistent_module(self):
+ try:
+ # assertRaises() clears __traceback__
+ import nonexistent_xyzzy
+ except ImportError as e:
+ tb = e.__traceback__
+ else:
+ self.fail("ImportError should have been raised")
+ self.assert_traceback(tb, [__file__])
+
+ def test_nonexistent_module_nested(self):
+ self.create_module("foo", "import nonexistent_xyzzy")
+ try:
+ import foo
+ except ImportError as e:
+ tb = e.__traceback__
+ else:
+ self.fail("ImportError should have been raised")
+ self.assert_traceback(tb, [__file__, 'foo.py'])
+
+ def test_exec_failure(self):
+ self.create_module("foo", "1/0")
+ try:
+ import foo
+ except ZeroDivisionError as e:
+ tb = e.__traceback__
+ else:
+ self.fail("ZeroDivisionError should have been raised")
+ self.assert_traceback(tb, [__file__, 'foo.py'])
+
+ def test_exec_failure_nested(self):
+ self.create_module("foo", "import bar")
+ self.create_module("bar", "1/0")
+ try:
+ import foo
+ except ZeroDivisionError as e:
+ tb = e.__traceback__
+ else:
+ self.fail("ZeroDivisionError should have been raised")
+ self.assert_traceback(tb, [__file__, 'foo.py', 'bar.py'])
+
+ @cpython_only
+ def test_import_bug(self):
+ # We simulate a bug in importlib and check that it's not stripped
+ # away from the traceback.
+ self.create_module("foo", "")
+ importlib = sys.modules['_frozen_importlib']
+ old_load_module = importlib.SourceLoader.load_module
+ try:
+ def load_module(*args):
+ 1/0
+ importlib.SourceLoader.load_module = load_module
+ try:
+ import foo
+ except ZeroDivisionError as e:
+ tb = e.__traceback__
+ else:
+ self.fail("ZeroDivisionError should have been raised")
+ self.assert_traceback(tb, [__file__, '<frozen importlib', __file__])
+ finally:
+ importlib.SourceLoader.load_module = old_load_module
+
+
def test_main(verbose=None):
flag = importlib_util.using___import__
try:
@@ -777,6 +869,7 @@ def test_main(verbose=None):
OverridingImportBuiltinTests,
ImportlibBootstrapTests,
TestSymbolicallyLinkedPackage,
+ ImportTracebackTests,
importlib_import_test_suite())
finally:
importlib_util.using___import__ = flag