Commit bbb6680e authored by Brett Cannon's avatar Brett Cannon

Have importlib take advantage of ImportError's new 'name' and 'path'

attributes.
parent 79ec55e9
...@@ -227,7 +227,7 @@ def _check_name(method): ...@@ -227,7 +227,7 @@ def _check_name(method):
""" """
def _check_name_wrapper(self, name, *args, **kwargs): def _check_name_wrapper(self, name, *args, **kwargs):
if self._name != name: if self._name != name:
raise ImportError("loader cannot handle %s" % name) raise ImportError("loader cannot handle %s" % name, name=name)
return method(self, name, *args, **kwargs) return method(self, name, *args, **kwargs)
_wrap(_check_name_wrapper, method) _wrap(_check_name_wrapper, method)
return _check_name_wrapper return _check_name_wrapper
...@@ -237,7 +237,8 @@ def _requires_builtin(fxn): ...@@ -237,7 +237,8 @@ def _requires_builtin(fxn):
"""Decorator to verify the named module is built-in.""" """Decorator to verify the named module is built-in."""
def _requires_builtin_wrapper(self, fullname): def _requires_builtin_wrapper(self, fullname):
if fullname not in sys.builtin_module_names: if fullname not in sys.builtin_module_names:
raise ImportError("{0} is not a built-in module".format(fullname)) raise ImportError("{0} is not a built-in module".format(fullname),
name=fullname)
return fxn(self, fullname) return fxn(self, fullname)
_wrap(_requires_builtin_wrapper, fxn) _wrap(_requires_builtin_wrapper, fxn)
return _requires_builtin_wrapper return _requires_builtin_wrapper
...@@ -247,7 +248,8 @@ def _requires_frozen(fxn): ...@@ -247,7 +248,8 @@ def _requires_frozen(fxn):
"""Decorator to verify the named module is frozen.""" """Decorator to verify the named module is frozen."""
def _requires_frozen_wrapper(self, fullname): def _requires_frozen_wrapper(self, fullname):
if not imp.is_frozen(fullname): if not imp.is_frozen(fullname):
raise ImportError("{0} is not a frozen module".format(fullname)) raise ImportError("{0} is not a frozen module".format(fullname),
name=fullname)
return fxn(self, fullname) return fxn(self, fullname)
_wrap(_requires_frozen_wrapper, fxn) _wrap(_requires_frozen_wrapper, fxn)
return _requires_frozen_wrapper return _requires_frozen_wrapper
...@@ -372,7 +374,7 @@ class _LoaderBasics: ...@@ -372,7 +374,7 @@ class _LoaderBasics:
filename = self.get_filename(fullname).rpartition(path_sep)[2] filename = self.get_filename(fullname).rpartition(path_sep)[2]
return filename.rsplit('.', 1)[0] == '__init__' return filename.rsplit('.', 1)[0] == '__init__'
def _bytes_from_bytecode(self, fullname, data, source_stats): def _bytes_from_bytecode(self, fullname, data, bytecode_path, source_stats):
"""Return the marshalled bytes from bytecode, verifying the magic """Return the marshalled bytes from bytecode, verifying the magic
number, timestamp and source size along the way. number, timestamp and source size along the way.
...@@ -383,7 +385,8 @@ class _LoaderBasics: ...@@ -383,7 +385,8 @@ class _LoaderBasics:
raw_timestamp = data[4:8] raw_timestamp = data[4:8]
raw_size = data[8:12] raw_size = data[8:12]
if len(magic) != 4 or magic != imp.get_magic(): if len(magic) != 4 or magic != imp.get_magic():
raise ImportError("bad magic number in {}".format(fullname)) raise ImportError("bad magic number in {}".format(fullname),
name=fullname, path=bytecode_path)
elif len(raw_timestamp) != 4: elif len(raw_timestamp) != 4:
raise EOFError("bad timestamp in {}".format(fullname)) raise EOFError("bad timestamp in {}".format(fullname))
elif len(raw_size) != 4: elif len(raw_size) != 4:
...@@ -396,7 +399,8 @@ class _LoaderBasics: ...@@ -396,7 +399,8 @@ class _LoaderBasics:
else: else:
if _r_long(raw_timestamp) != source_mtime: if _r_long(raw_timestamp) != source_mtime:
raise ImportError( raise ImportError(
"bytecode is stale for {}".format(fullname)) "bytecode is stale for {}".format(fullname),
name=fullname, path=bytecode_path)
try: try:
source_size = source_stats['size'] & 0xFFFFFFFF source_size = source_stats['size'] & 0xFFFFFFFF
except KeyError: except KeyError:
...@@ -404,7 +408,8 @@ class _LoaderBasics: ...@@ -404,7 +408,8 @@ class _LoaderBasics:
else: else:
if _r_long(raw_size) != source_size: if _r_long(raw_size) != source_size:
raise ImportError( raise ImportError(
"bytecode is stale for {}".format(fullname)) "bytecode is stale for {}".format(fullname),
name=fullname, path=bytecode_path)
# Can't return the code object as errors from marshal loading need to # Can't return the code object as errors from marshal loading need to
# propagate even when source is available. # propagate even when source is available.
return data[12:] return data[12:]
...@@ -466,7 +471,8 @@ class SourceLoader(_LoaderBasics): ...@@ -466,7 +471,8 @@ class SourceLoader(_LoaderBasics):
try: try:
source_bytes = self.get_data(path) source_bytes = self.get_data(path)
except IOError: except IOError:
raise ImportError("source not available through get_data()") raise ImportError("source not available through get_data()",
name=fullname)
encoding = tokenize.detect_encoding(_io.BytesIO(source_bytes).readline) encoding = tokenize.detect_encoding(_io.BytesIO(source_bytes).readline)
newline_decoder = _io.IncrementalNewlineDecoder(None, True) newline_decoder = _io.IncrementalNewlineDecoder(None, True)
return newline_decoder.decode(source_bytes.decode(encoding[0])) return newline_decoder.decode(source_bytes.decode(encoding[0]))
...@@ -495,6 +501,7 @@ class SourceLoader(_LoaderBasics): ...@@ -495,6 +501,7 @@ class SourceLoader(_LoaderBasics):
else: else:
try: try:
bytes_data = self._bytes_from_bytecode(fullname, data, bytes_data = self._bytes_from_bytecode(fullname, data,
bytecode_path,
st) st)
except (ImportError, EOFError): except (ImportError, EOFError):
pass pass
...@@ -505,7 +512,8 @@ class SourceLoader(_LoaderBasics): ...@@ -505,7 +512,8 @@ class SourceLoader(_LoaderBasics):
return found return found
else: else:
msg = "Non-code object in {}" msg = "Non-code object in {}"
raise ImportError(msg.format(bytecode_path)) raise ImportError(msg.format(bytecode_path),
name=fullname, path=bytecode_path)
source_bytes = self.get_data(source_path) source_bytes = self.get_data(source_path)
code_object = compile(source_bytes, source_path, 'exec', code_object = compile(source_bytes, source_path, 'exec',
dont_inherit=True) dont_inherit=True)
...@@ -604,12 +612,13 @@ class _SourcelessFileLoader(_FileLoader, _LoaderBasics): ...@@ -604,12 +612,13 @@ class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
def get_code(self, fullname): def get_code(self, fullname):
path = self.get_filename(fullname) path = self.get_filename(fullname)
data = self.get_data(path) data = self.get_data(path)
bytes_data = self._bytes_from_bytecode(fullname, data, None) bytes_data = self._bytes_from_bytecode(fullname, data, path, None)
found = marshal.loads(bytes_data) found = marshal.loads(bytes_data)
if isinstance(found, code_type): if isinstance(found, code_type):
return found return found
else: else:
raise ImportError("Non-code object in {}".format(path)) raise ImportError("Non-code object in {}".format(path),
name=fullname, path=path)
def get_source(self, fullname): def get_source(self, fullname):
"""Return None as there is no source code.""" """Return None as there is no source code."""
...@@ -678,7 +687,8 @@ class PathFinder: ...@@ -678,7 +687,8 @@ class PathFinder:
except ImportError: except ImportError:
continue continue
else: else:
raise ImportError("no path hook found for {0}".format(path)) raise ImportError("no path hook found for {0}".format(path),
path=path)
@classmethod @classmethod
def _path_importer_cache(cls, path, default=None): def _path_importer_cache(cls, path, default=None):
...@@ -836,7 +846,7 @@ def _file_path_hook(path): ...@@ -836,7 +846,7 @@ def _file_path_hook(path):
_SourceFinderDetails(), _SourceFinderDetails(),
_SourcelessFinderDetails()) _SourcelessFinderDetails())
else: else:
raise ImportError("only directories are supported") raise ImportError("only directories are supported", path=path)
_DEFAULT_PATH_HOOK = _file_path_hook _DEFAULT_PATH_HOOK = _file_path_hook
...@@ -936,10 +946,10 @@ def _find_and_load(name, import_): ...@@ -936,10 +946,10 @@ def _find_and_load(name, import_):
path = parent_module.__path__ path = parent_module.__path__
except AttributeError: except AttributeError:
msg = (_ERR_MSG + '; {} is not a package').format(name, parent) msg = (_ERR_MSG + '; {} is not a package').format(name, parent)
raise ImportError(msg) raise ImportError(msg, name=name)
loader = _find_module(name, path) loader = _find_module(name, path)
if loader is None: if loader is None:
raise ImportError(_ERR_MSG.format(name)) raise ImportError(_ERR_MSG.format(name), name=name)
elif name not in sys.modules: elif name not in sys.modules:
# The parent import may have already imported this module. # The parent import may have already imported this module.
loader.load_module(name) loader.load_module(name)
...@@ -978,7 +988,7 @@ def _gcd_import(name, package=None, level=0): ...@@ -978,7 +988,7 @@ def _gcd_import(name, package=None, level=0):
if module is None: if module is None:
message = ("import of {} halted; " message = ("import of {} halted; "
"None in sys.modules".format(name)) "None in sys.modules".format(name))
raise ImportError(message) raise ImportError(message, name=name)
return module return module
except KeyError: except KeyError:
pass # Don't want to chain the exception pass # Don't want to chain the exception
......
...@@ -207,7 +207,7 @@ class PyLoader(SourceLoader): ...@@ -207,7 +207,7 @@ class PyLoader(SourceLoader):
DeprecationWarning) DeprecationWarning)
path = self.source_path(fullname) path = self.source_path(fullname)
if path is None: if path is None:
raise ImportError raise ImportError(name=fullname)
else: else:
return path return path
...@@ -235,7 +235,7 @@ class PyPycLoader(PyLoader): ...@@ -235,7 +235,7 @@ class PyPycLoader(PyLoader):
if path is not None: if path is not None:
return path return path
raise ImportError("no source or bytecode path available for " raise ImportError("no source or bytecode path available for "
"{0!r}".format(fullname)) "{0!r}".format(fullname), name=fullname)
def get_code(self, fullname): def get_code(self, fullname):
"""Get a code object from source or bytecode.""" """Get a code object from source or bytecode."""
...@@ -253,7 +253,8 @@ class PyPycLoader(PyLoader): ...@@ -253,7 +253,8 @@ class PyPycLoader(PyLoader):
magic = data[:4] magic = data[:4]
if len(magic) < 4: if len(magic) < 4:
raise ImportError( raise ImportError(
"bad magic number in {}".format(fullname)) "bad magic number in {}".format(fullname),
name=fullname, path=bytecode_path)
raw_timestamp = data[4:8] raw_timestamp = data[4:8]
if len(raw_timestamp) < 4: if len(raw_timestamp) < 4:
raise EOFError("bad timestamp in {}".format(fullname)) raise EOFError("bad timestamp in {}".format(fullname))
...@@ -262,12 +263,14 @@ class PyPycLoader(PyLoader): ...@@ -262,12 +263,14 @@ class PyPycLoader(PyLoader):
# Verify that the magic number is valid. # Verify that the magic number is valid.
if imp.get_magic() != magic: if imp.get_magic() != magic:
raise ImportError( raise ImportError(
"bad magic number in {}".format(fullname)) "bad magic number in {}".format(fullname),
name=fullname, path=bytecode_path)
# Verify that the bytecode is not stale (only matters when # Verify that the bytecode is not stale (only matters when
# there is source to fall back on. # there is source to fall back on.
if source_timestamp: if source_timestamp:
if pyc_timestamp < source_timestamp: if pyc_timestamp < source_timestamp:
raise ImportError("bytecode is stale") raise ImportError("bytecode is stale", name=fullname,
path=bytecode_path)
except (ImportError, EOFError): except (ImportError, EOFError):
# If source is available give it a shot. # If source is available give it a shot.
if source_timestamp is not None: if source_timestamp is not None:
...@@ -279,12 +282,13 @@ class PyPycLoader(PyLoader): ...@@ -279,12 +282,13 @@ class PyPycLoader(PyLoader):
return marshal.loads(bytecode) return marshal.loads(bytecode)
elif source_timestamp is None: elif source_timestamp is None:
raise ImportError("no source or bytecode available to create code " raise ImportError("no source or bytecode available to create code "
"object for {0!r}".format(fullname)) "object for {0!r}".format(fullname),
name=fullname)
# Use the source. # Use the source.
source_path = self.source_path(fullname) source_path = self.source_path(fullname)
if source_path is None: if source_path is None:
message = "a source path must exist to load {0}".format(fullname) message = "a source path must exist to load {0}".format(fullname)
raise ImportError(message) raise ImportError(message, name=fullname)
source = self.get_data(source_path) source = self.get_data(source_path)
code_object = compile(source, source_path, 'exec', dont_inherit=True) code_object = compile(source, source_path, 'exec', dont_inherit=True)
# Generate bytecode and write it out. # Generate bytecode and write it out.
......
...@@ -54,15 +54,17 @@ class LoaderTests(abc.LoaderTests): ...@@ -54,15 +54,17 @@ class LoaderTests(abc.LoaderTests):
def test_unloadable(self): def test_unloadable(self):
name = 'dssdsdfff' name = 'dssdsdfff'
assert name not in sys.builtin_module_names assert name not in sys.builtin_module_names
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.load_module(name) self.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_already_imported(self): def test_already_imported(self):
# Using the name of a module already imported but not a built-in should # Using the name of a module already imported but not a built-in should
# still fail. # still fail.
assert hasattr(importlib, '__file__') # Not a built-in. assert hasattr(importlib, '__file__') # Not a built-in.
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.load_module('importlib') self.load_module('importlib')
self.assertEqual(cm.exception.name, 'importlib')
class InspectLoaderTests(unittest.TestCase): class InspectLoaderTests(unittest.TestCase):
...@@ -88,8 +90,9 @@ class InspectLoaderTests(unittest.TestCase): ...@@ -88,8 +90,9 @@ class InspectLoaderTests(unittest.TestCase):
# Modules not built-in should raise ImportError. # Modules not built-in should raise ImportError.
for meth_name in ('get_code', 'get_source', 'is_package'): for meth_name in ('get_code', 'get_source', 'is_package'):
method = getattr(machinery.BuiltinImporter, meth_name) method = getattr(machinery.BuiltinImporter, meth_name)
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
method(builtin_util.BAD_NAME) method(builtin_util.BAD_NAME)
self.assertRaises(builtin_util.BAD_NAME)
......
...@@ -46,8 +46,10 @@ class LoaderTests(abc.LoaderTests): ...@@ -46,8 +46,10 @@ class LoaderTests(abc.LoaderTests):
pass pass
def test_unloadable(self): def test_unloadable(self):
with self.assertRaises(ImportError): name = 'asdfjkl;'
self.load_module('asdfjkl;') with self.assertRaises(ImportError) as cm:
self.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_main(): def test_main():
......
...@@ -57,8 +57,9 @@ class LoaderTests(abc.LoaderTests): ...@@ -57,8 +57,9 @@ class LoaderTests(abc.LoaderTests):
def test_unloadable(self): def test_unloadable(self):
assert machinery.FrozenImporter.find_module('_not_real') is None assert machinery.FrozenImporter.find_module('_not_real') is None
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
machinery.FrozenImporter.load_module('_not_real') machinery.FrozenImporter.load_module('_not_real')
self.assertEqual(cm.exception.name, '_not_real')
class InspectLoaderTests(unittest.TestCase): class InspectLoaderTests(unittest.TestCase):
...@@ -92,8 +93,9 @@ class InspectLoaderTests(unittest.TestCase): ...@@ -92,8 +93,9 @@ class InspectLoaderTests(unittest.TestCase):
# Raise ImportError for modules that are not frozen. # Raise ImportError for modules that are not frozen.
for meth_name in ('get_code', 'get_source', 'is_package'): for meth_name in ('get_code', 'get_source', 'is_package'):
method = getattr(machinery.FrozenImporter, meth_name) method = getattr(machinery.FrozenImporter, meth_name)
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
method('importlib') method('importlib')
self.assertEqual(cm.exception.name, 'importlib')
def test_main(): def test_main():
......
...@@ -34,8 +34,9 @@ class UseCache(unittest.TestCase): ...@@ -34,8 +34,9 @@ class UseCache(unittest.TestCase):
name = 'using_None' name = 'using_None'
with util.uncache(name): with util.uncache(name):
sys.modules[name] = None sys.modules[name] = None
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
import_util.import_(name) import_util.import_(name)
self.assertEqual(cm.exception.name, name)
def create_mock(self, *names, return_=None): def create_mock(self, *names, return_=None):
mock = util.mock_modules(*names) mock = util.mock_modules(*names)
......
...@@ -19,14 +19,16 @@ class ParentModuleTests(unittest.TestCase): ...@@ -19,14 +19,16 @@ class ParentModuleTests(unittest.TestCase):
def test_bad_parent(self): def test_bad_parent(self):
with util.mock_modules('pkg.module') as mock: with util.mock_modules('pkg.module') as mock:
with util.import_state(meta_path=[mock]): with util.import_state(meta_path=[mock]):
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
import_util.import_('pkg.module') import_util.import_('pkg.module')
self.assertEqual(cm.exception.name, 'pkg')
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__')
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
import_util.import_('sys.no_submodules_here') import_util.import_('sys.no_submodules_here')
self.assertEqual(cm.exception.name, 'sys.no_submodules_here')
def test_module_not_package_but_side_effects(self): def test_module_not_package_but_side_effects(self):
# If a module injects something into sys.modules as a side-effect, then # If a module injects something into sys.modules as a side-effect, then
......
...@@ -471,8 +471,9 @@ class BadBytecodeFailureTests(unittest.TestCase): ...@@ -471,8 +471,9 @@ class BadBytecodeFailureTests(unittest.TestCase):
{'path': os.path.join('path', 'to', 'mod'), {'path': os.path.join('path', 'to', 'mod'),
'magic': bad_magic}} 'magic': bad_magic}}
mock = PyPycLoaderMock({name: None}, bc) mock = PyPycLoaderMock({name: None}, bc)
with util.uncache(name), self.assertRaises(ImportError): with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name) mock.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_no_bytecode(self): def test_no_bytecode(self):
# Missing code object bytecode should lead to an EOFError. # Missing code object bytecode should lead to an EOFError.
...@@ -516,8 +517,9 @@ class MissingPathsTests(unittest.TestCase): ...@@ -516,8 +517,9 @@ class MissingPathsTests(unittest.TestCase):
# If all *_path methods return None, raise ImportError. # If all *_path methods return None, raise ImportError.
name = 'mod' name = 'mod'
mock = PyPycLoaderMock({name: None}) mock = PyPycLoaderMock({name: None})
with util.uncache(name), self.assertRaises(ImportError): with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name) mock.load_module(name)
self.assertEqual(cm.exception.name, name)
def test_source_path_ImportError(self): def test_source_path_ImportError(self):
# An ImportError from source_path should trigger an ImportError. # An ImportError from source_path should trigger an ImportError.
...@@ -533,7 +535,7 @@ class MissingPathsTests(unittest.TestCase): ...@@ -533,7 +535,7 @@ class MissingPathsTests(unittest.TestCase):
mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')}) mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
bad_meth = types.MethodType(raise_ImportError, mock) bad_meth = types.MethodType(raise_ImportError, mock)
mock.bytecode_path = bad_meth mock.bytecode_path = bad_meth
with util.uncache(name), self.assertRaises(ImportError): with util.uncache(name), self.assertRaises(ImportError) as cm:
mock.load_module(name) mock.load_module(name)
...@@ -594,8 +596,9 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness): ...@@ -594,8 +596,9 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness):
def raise_IOError(path): def raise_IOError(path):
raise IOError raise IOError
self.loader.get_data = raise_IOError self.loader.get_data = raise_IOError
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.loader.get_source(self.name) self.loader.get_source(self.name)
self.assertEqual(cm.exception.name, self.name)
def test_is_package(self): def test_is_package(self):
# Properly detect when loading a package. # Properly detect when loading a package.
......
...@@ -232,8 +232,10 @@ class BadBytecodeTest(unittest.TestCase): ...@@ -232,8 +232,10 @@ class BadBytecodeTest(unittest.TestCase):
lambda bc: bc[:12] + marshal.dumps(b'abcd'), lambda bc: bc[:12] + marshal.dumps(b'abcd'),
del_source=del_source) del_source=del_source)
file_path = mapping['_temp'] if not del_source else bytecode_path file_path = mapping['_temp'] if not del_source else bytecode_path
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.import_(file_path, '_temp') self.import_(file_path, '_temp')
self.assertEqual(cm.exception.name, '_temp')
self.assertEqual(cm.exception.path, bytecode_path)
def _test_bad_marshal(self, *, del_source=False): def _test_bad_marshal(self, *, del_source=False):
with source_util.create_modules('_temp') as mapping: with source_util.create_modules('_temp') as mapping:
...@@ -381,15 +383,19 @@ class SourcelessLoaderBadBytecodeTest(BadBytecodeTest): ...@@ -381,15 +383,19 @@ class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
def test_empty_file(self): def test_empty_file(self):
def test(name, mapping, bytecode_path): def test(name, mapping, bytecode_path):
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.import_(bytecode_path, name) self.import_(bytecode_path, name)
self.assertEqual(cm.exception.name, name)
self.assertEqual(cm.exception.path, bytecode_path)
self._test_empty_file(test, del_source=True) self._test_empty_file(test, del_source=True)
def test_partial_magic(self): def test_partial_magic(self):
def test(name, mapping, bytecode_path): def test(name, mapping, bytecode_path):
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.import_(bytecode_path, name) self.import_(bytecode_path, name)
self.assertEqual(cm.exception.name, name)
self.assertEqual(cm.exception.path, bytecode_path)
self._test_partial_magic(test, del_source=True) self._test_partial_magic(test, del_source=True)
def test_magic_only(self): def test_magic_only(self):
...@@ -401,8 +407,10 @@ class SourcelessLoaderBadBytecodeTest(BadBytecodeTest): ...@@ -401,8 +407,10 @@ class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
def test_bad_magic(self): def test_bad_magic(self):
def test(name, mapping, bytecode_path): def test(name, mapping, bytecode_path):
with self.assertRaises(ImportError): with self.assertRaises(ImportError) as cm:
self.import_(bytecode_path, name) self.import_(bytecode_path, name)
self.assertEqual(cm.exception.name, name)
self.assertEqual(cm.exception.path, bytecode_path)
self._test_bad_magic(test, del_source=True) self._test_bad_magic(test, del_source=True)
......
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