Back to index

python3.2  3.2.2
test_abc_loader.py
Go to the documentation of this file.
00001 import importlib
00002 from importlib import abc
00003 
00004 from .. import abc as testing_abc
00005 from .. import util
00006 from . import util as source_util
00007 
00008 import imp
00009 import inspect
00010 import io
00011 import marshal
00012 import os
00013 import sys
00014 import types
00015 import unittest
00016 import warnings
00017 
00018 
00019 class SourceOnlyLoaderMock(abc.SourceLoader):
00020 
00021     # Globals that should be defined for all modules.
00022     source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, "
00023               b"repr(__loader__)])")
00024 
00025     def __init__(self, path):
00026         self.path = path
00027 
00028     def get_data(self, path):
00029         assert self.path == path
00030         return self.source
00031 
00032     def get_filename(self, fullname):
00033         return self.path
00034 
00035 
00036 class SourceLoaderMock(SourceOnlyLoaderMock):
00037 
00038     source_mtime = 1
00039 
00040     def __init__(self, path, magic=imp.get_magic()):
00041         super().__init__(path)
00042         self.bytecode_path = imp.cache_from_source(self.path)
00043         data = bytearray(magic)
00044         data.extend(marshal._w_long(self.source_mtime))
00045         code_object = compile(self.source, self.path, 'exec',
00046                                 dont_inherit=True)
00047         data.extend(marshal.dumps(code_object))
00048         self.bytecode = bytes(data)
00049         self.written = {}
00050 
00051     def get_data(self, path):
00052         if path == self.path:
00053             return super().get_data(path)
00054         elif path == self.bytecode_path:
00055             return self.bytecode
00056         else:
00057             raise IOError
00058 
00059     def path_mtime(self, path):
00060         assert path == self.path
00061         return self.source_mtime
00062 
00063     def set_data(self, path, data):
00064         self.written[path] = bytes(data)
00065         return path == self.bytecode_path
00066 
00067 
00068 class PyLoaderMock(abc.PyLoader):
00069 
00070     # Globals that should be defined for all modules.
00071     source = (b"_ = '::'.join([__name__, __file__, __package__, "
00072               b"repr(__loader__)])")
00073 
00074     def __init__(self, data):
00075         """Take a dict of 'module_name: path' pairings.
00076 
00077         Paths should have no file extension, allowing packages to be denoted by
00078         ending in '__init__'.
00079 
00080         """
00081         self.module_paths = data
00082         self.path_to_module = {val:key for key,val in data.items()}
00083 
00084     def get_data(self, path):
00085         if path not in self.path_to_module:
00086             raise IOError
00087         return self.source
00088 
00089     def is_package(self, name):
00090         filename = os.path.basename(self.get_filename(name))
00091         return os.path.splitext(filename)[0] == '__init__'
00092 
00093     def source_path(self, name):
00094         try:
00095             return self.module_paths[name]
00096         except KeyError:
00097             raise ImportError
00098 
00099     def get_filename(self, name):
00100         """Silence deprecation warning."""
00101         with warnings.catch_warnings(record=True) as w:
00102             warnings.simplefilter("always")
00103             path = super().get_filename(name)
00104             assert len(w) == 1
00105             assert issubclass(w[0].category, PendingDeprecationWarning)
00106             return path
00107 
00108 
00109 class PyLoaderCompatMock(PyLoaderMock):
00110 
00111     """Mock that matches what is suggested to have a loader that is compatible
00112     from Python 3.1 onwards."""
00113 
00114     def get_filename(self, fullname):
00115         try:
00116             return self.module_paths[fullname]
00117         except KeyError:
00118             raise ImportError
00119 
00120     def source_path(self, fullname):
00121         try:
00122             return self.get_filename(fullname)
00123         except ImportError:
00124             return None
00125 
00126 
00127 class PyPycLoaderMock(abc.PyPycLoader, PyLoaderMock):
00128 
00129     default_mtime = 1
00130 
00131     def __init__(self, source, bc={}):
00132         """Initialize mock.
00133 
00134         'bc' is a dict keyed on a module's name. The value is dict with
00135         possible keys of 'path', 'mtime', 'magic', and 'bc'. Except for 'path',
00136         each of those keys control if any part of created bytecode is to
00137         deviate from default values.
00138 
00139         """
00140         super().__init__(source)
00141         self.module_bytecode = {}
00142         self.path_to_bytecode = {}
00143         self.bytecode_to_path = {}
00144         for name, data in bc.items():
00145             self.path_to_bytecode[data['path']] = name
00146             self.bytecode_to_path[name] = data['path']
00147             magic = data.get('magic', imp.get_magic())
00148             mtime = importlib._w_long(data.get('mtime', self.default_mtime))
00149             if 'bc' in data:
00150                 bc = data['bc']
00151             else:
00152                 bc = self.compile_bc(name)
00153             self.module_bytecode[name] = magic + mtime + bc
00154 
00155     def compile_bc(self, name):
00156         source_path = self.module_paths.get(name, '<test>') or '<test>'
00157         code = compile(self.source, source_path, 'exec')
00158         return marshal.dumps(code)
00159 
00160     def source_mtime(self, name):
00161         if name in self.module_paths:
00162             return self.default_mtime
00163         elif name in self.module_bytecode:
00164             return None
00165         else:
00166             raise ImportError
00167 
00168     def bytecode_path(self, name):
00169         try:
00170             return self.bytecode_to_path[name]
00171         except KeyError:
00172             if name in self.module_paths:
00173                 return None
00174             else:
00175                 raise ImportError
00176 
00177     def write_bytecode(self, name, bytecode):
00178         self.module_bytecode[name] = bytecode
00179         return True
00180 
00181     def get_data(self, path):
00182         if path in self.path_to_module:
00183             return super().get_data(path)
00184         elif path in self.path_to_bytecode:
00185             name = self.path_to_bytecode[path]
00186             return self.module_bytecode[name]
00187         else:
00188             raise IOError
00189 
00190     def is_package(self, name):
00191         try:
00192             return super().is_package(name)
00193         except TypeError:
00194             return '__init__' in self.bytecode_to_path[name]
00195 
00196     def get_code(self, name):
00197         with warnings.catch_warnings(record=True) as w:
00198             warnings.simplefilter("always")
00199             code_object = super().get_code(name)
00200             assert len(w) == 1
00201             assert issubclass(w[0].category, PendingDeprecationWarning)
00202             return code_object
00203 
00204 class PyLoaderTests(testing_abc.LoaderTests):
00205 
00206     """Tests for importlib.abc.PyLoader."""
00207 
00208     mocker = PyLoaderMock
00209 
00210     def eq_attrs(self, ob, **kwargs):
00211         for attr, val in kwargs.items():
00212             found = getattr(ob, attr)
00213             self.assertEqual(found, val,
00214                     "{} attribute: {} != {}".format(attr, found, val))
00215 
00216     def test_module(self):
00217         name = '<module>'
00218         path = os.path.join('', 'path', 'to', 'module')
00219         mock = self.mocker({name: path})
00220         with util.uncache(name):
00221             module = mock.load_module(name)
00222             self.assertTrue(name in sys.modules)
00223         self.eq_attrs(module, __name__=name, __file__=path, __package__='',
00224                         __loader__=mock)
00225         self.assertTrue(not hasattr(module, '__path__'))
00226         return mock, name
00227 
00228     def test_package(self):
00229         name = '<pkg>'
00230         path = os.path.join('path', 'to', name, '__init__')
00231         mock = self.mocker({name: path})
00232         with util.uncache(name):
00233             module = mock.load_module(name)
00234             self.assertTrue(name in sys.modules)
00235         self.eq_attrs(module, __name__=name, __file__=path,
00236                 __path__=[os.path.dirname(path)], __package__=name,
00237                 __loader__=mock)
00238         return mock, name
00239 
00240     def test_lacking_parent(self):
00241         name = 'pkg.mod'
00242         path = os.path.join('path', 'to', 'pkg', 'mod')
00243         mock = self.mocker({name: path})
00244         with util.uncache(name):
00245             module = mock.load_module(name)
00246             self.assertIn(name, sys.modules)
00247         self.eq_attrs(module, __name__=name, __file__=path, __package__='pkg',
00248                         __loader__=mock)
00249         self.assertFalse(hasattr(module, '__path__'))
00250         return mock, name
00251 
00252     def test_module_reuse(self):
00253         name = 'mod'
00254         path = os.path.join('path', 'to', 'mod')
00255         module = imp.new_module(name)
00256         mock = self.mocker({name: path})
00257         with util.uncache(name):
00258             sys.modules[name] = module
00259             loaded_module = mock.load_module(name)
00260             self.assertTrue(loaded_module is module)
00261             self.assertTrue(sys.modules[name] is module)
00262         return mock, name
00263 
00264     def test_state_after_failure(self):
00265         name = "mod"
00266         module = imp.new_module(name)
00267         module.blah = None
00268         mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
00269         mock.source = b"1/0"
00270         with util.uncache(name):
00271             sys.modules[name] = module
00272             with self.assertRaises(ZeroDivisionError):
00273                 mock.load_module(name)
00274             self.assertTrue(sys.modules[name] is module)
00275             self.assertTrue(hasattr(module, 'blah'))
00276         return mock
00277 
00278     def test_unloadable(self):
00279         name = "mod"
00280         mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
00281         mock.source = b"1/0"
00282         with util.uncache(name):
00283             with self.assertRaises(ZeroDivisionError):
00284                 mock.load_module(name)
00285             self.assertTrue(name not in sys.modules)
00286         return mock
00287 
00288 
00289 class PyLoaderCompatTests(PyLoaderTests):
00290 
00291     """Test that the suggested code to make a loader that is compatible from
00292     Python 3.1 forward works."""
00293 
00294     mocker = PyLoaderCompatMock
00295 
00296 
00297 class PyLoaderInterfaceTests(unittest.TestCase):
00298 
00299     """Tests for importlib.abc.PyLoader to make sure that when source_path()
00300     doesn't return a path everything works as expected."""
00301 
00302     def test_no_source_path(self):
00303         # No source path should lead to ImportError.
00304         name = 'mod'
00305         mock = PyLoaderMock({})
00306         with util.uncache(name), self.assertRaises(ImportError):
00307             mock.load_module(name)
00308 
00309     def test_source_path_is_None(self):
00310         name = 'mod'
00311         mock = PyLoaderMock({name: None})
00312         with util.uncache(name), self.assertRaises(ImportError):
00313             mock.load_module(name)
00314 
00315     def test_get_filename_with_source_path(self):
00316         # get_filename() should return what source_path() returns.
00317         name = 'mod'
00318         path = os.path.join('path', 'to', 'source')
00319         mock = PyLoaderMock({name: path})
00320         with util.uncache(name):
00321             self.assertEqual(mock.get_filename(name), path)
00322 
00323     def test_get_filename_no_source_path(self):
00324         # get_filename() should raise ImportError if source_path returns None.
00325         name = 'mod'
00326         mock = PyLoaderMock({name: None})
00327         with util.uncache(name), self.assertRaises(ImportError):
00328             mock.get_filename(name)
00329 
00330 
00331 class PyPycLoaderTests(PyLoaderTests):
00332 
00333     """Tests for importlib.abc.PyPycLoader."""
00334 
00335     mocker = PyPycLoaderMock
00336 
00337     @source_util.writes_bytecode_files
00338     def verify_bytecode(self, mock, name):
00339         assert name in mock.module_paths
00340         self.assertIn(name, mock.module_bytecode)
00341         magic = mock.module_bytecode[name][:4]
00342         self.assertEqual(magic, imp.get_magic())
00343         mtime = importlib._r_long(mock.module_bytecode[name][4:8])
00344         self.assertEqual(mtime, 1)
00345         bc = mock.module_bytecode[name][8:]
00346         self.assertEqual(bc, mock.compile_bc(name))
00347 
00348     def test_module(self):
00349         mock, name = super().test_module()
00350         self.verify_bytecode(mock, name)
00351 
00352     def test_package(self):
00353         mock, name = super().test_package()
00354         self.verify_bytecode(mock, name)
00355 
00356     def test_lacking_parent(self):
00357         mock, name = super().test_lacking_parent()
00358         self.verify_bytecode(mock, name)
00359 
00360     def test_module_reuse(self):
00361         mock, name = super().test_module_reuse()
00362         self.verify_bytecode(mock, name)
00363 
00364     def test_state_after_failure(self):
00365         super().test_state_after_failure()
00366 
00367     def test_unloadable(self):
00368         super().test_unloadable()
00369 
00370 
00371 class PyPycLoaderInterfaceTests(unittest.TestCase):
00372 
00373     """Test for the interface of importlib.abc.PyPycLoader."""
00374 
00375     def get_filename_check(self, src_path, bc_path, expect):
00376         name = 'mod'
00377         mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}})
00378         with util.uncache(name):
00379             assert mock.source_path(name) == src_path
00380             assert mock.bytecode_path(name) == bc_path
00381             self.assertEqual(mock.get_filename(name), expect)
00382 
00383     def test_filename_with_source_bc(self):
00384         # When source and bytecode paths present, return the source path.
00385         self.get_filename_check('source_path', 'bc_path', 'source_path')
00386 
00387     def test_filename_with_source_no_bc(self):
00388         # With source but no bc, return source path.
00389         self.get_filename_check('source_path', None, 'source_path')
00390 
00391     def test_filename_with_no_source_bc(self):
00392         # With not source but bc, return the bc path.
00393         self.get_filename_check(None, 'bc_path', 'bc_path')
00394 
00395     def test_filename_with_no_source_or_bc(self):
00396         # With no source or bc, raise ImportError.
00397         name = 'mod'
00398         mock = PyPycLoaderMock({name: None}, {name: {'path': None}})
00399         with util.uncache(name), self.assertRaises(ImportError):
00400             mock.get_filename(name)
00401 
00402 
00403 class SkipWritingBytecodeTests(unittest.TestCase):
00404 
00405     """Test that bytecode is properly handled based on
00406     sys.dont_write_bytecode."""
00407 
00408     @source_util.writes_bytecode_files
00409     def run_test(self, dont_write_bytecode):
00410         name = 'mod'
00411         mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
00412         sys.dont_write_bytecode = dont_write_bytecode
00413         with util.uncache(name):
00414             mock.load_module(name)
00415         self.assertTrue((name in mock.module_bytecode) is not
00416                         dont_write_bytecode)
00417 
00418     def test_no_bytecode_written(self):
00419         self.run_test(True)
00420 
00421     def test_bytecode_written(self):
00422         self.run_test(False)
00423 
00424 
00425 class RegeneratedBytecodeTests(unittest.TestCase):
00426 
00427     """Test that bytecode is regenerated as expected."""
00428 
00429     @source_util.writes_bytecode_files
00430     def test_different_magic(self):
00431         # A different magic number should lead to new bytecode.
00432         name = 'mod'
00433         bad_magic = b'\x00\x00\x00\x00'
00434         assert bad_magic != imp.get_magic()
00435         mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
00436                                 {name: {'path': os.path.join('path', 'to',
00437                                                     'mod.bytecode'),
00438                                         'magic': bad_magic}})
00439         with util.uncache(name):
00440             mock.load_module(name)
00441         self.assertTrue(name in mock.module_bytecode)
00442         magic = mock.module_bytecode[name][:4]
00443         self.assertEqual(magic, imp.get_magic())
00444 
00445     @source_util.writes_bytecode_files
00446     def test_old_mtime(self):
00447         # Bytecode with an older mtime should be regenerated.
00448         name = 'mod'
00449         old_mtime = PyPycLoaderMock.default_mtime - 1
00450         mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
00451                 {name: {'path': 'path/to/mod.bytecode', 'mtime': old_mtime}})
00452         with util.uncache(name):
00453             mock.load_module(name)
00454         self.assertTrue(name in mock.module_bytecode)
00455         mtime = importlib._r_long(mock.module_bytecode[name][4:8])
00456         self.assertEqual(mtime, PyPycLoaderMock.default_mtime)
00457 
00458 
00459 class BadBytecodeFailureTests(unittest.TestCase):
00460 
00461     """Test import failures when there is no source and parts of the bytecode
00462     is bad."""
00463 
00464     def test_bad_magic(self):
00465         # A bad magic number should lead to an ImportError.
00466         name = 'mod'
00467         bad_magic = b'\x00\x00\x00\x00'
00468         bc = {name:
00469                 {'path': os.path.join('path', 'to', 'mod'),
00470                  'magic': bad_magic}}
00471         mock = PyPycLoaderMock({name: None}, bc)
00472         with util.uncache(name), self.assertRaises(ImportError):
00473             mock.load_module(name)
00474 
00475     def test_no_bytecode(self):
00476         # Missing code object bytecode should lead to an EOFError.
00477         name = 'mod'
00478         bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b''}}
00479         mock = PyPycLoaderMock({name: None}, bc)
00480         with util.uncache(name), self.assertRaises(EOFError):
00481             mock.load_module(name)
00482 
00483     def test_bad_bytecode(self):
00484         # Malformed code object bytecode should lead to a ValueError.
00485         name = 'mod'
00486         bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b'1234'}}
00487         mock = PyPycLoaderMock({name: None}, bc)
00488         with util.uncache(name), self.assertRaises(ValueError):
00489             mock.load_module(name)
00490 
00491 
00492 def raise_ImportError(*args, **kwargs):
00493     raise ImportError
00494 
00495 class MissingPathsTests(unittest.TestCase):
00496 
00497     """Test what happens when a source or bytecode path does not exist (either
00498     from *_path returning None or raising ImportError)."""
00499 
00500     def test_source_path_None(self):
00501         # Bytecode should be used when source_path returns None, along with
00502         # __file__ being set to the bytecode path.
00503         name = 'mod'
00504         bytecode_path = 'path/to/mod'
00505         mock = PyPycLoaderMock({name: None}, {name: {'path': bytecode_path}})
00506         with util.uncache(name):
00507             module = mock.load_module(name)
00508         self.assertEqual(module.__file__, bytecode_path)
00509 
00510     # Testing for bytecode_path returning None handled by all tests where no
00511     # bytecode initially exists.
00512 
00513     def test_all_paths_None(self):
00514         # If all *_path methods return None, raise ImportError.
00515         name = 'mod'
00516         mock = PyPycLoaderMock({name: None})
00517         with util.uncache(name), self.assertRaises(ImportError):
00518             mock.load_module(name)
00519 
00520     def test_source_path_ImportError(self):
00521         # An ImportError from source_path should trigger an ImportError.
00522         name = 'mod'
00523         mock = PyPycLoaderMock({}, {name: {'path': os.path.join('path', 'to',
00524                                                                 'mod')}})
00525         with util.uncache(name), self.assertRaises(ImportError):
00526             mock.load_module(name)
00527 
00528     def test_bytecode_path_ImportError(self):
00529         # An ImportError from bytecode_path should trigger an ImportError.
00530         name = 'mod'
00531         mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
00532         bad_meth = types.MethodType(raise_ImportError, mock)
00533         mock.bytecode_path = bad_meth
00534         with util.uncache(name), self.assertRaises(ImportError):
00535             mock.load_module(name)
00536 
00537 
00538 class SourceLoaderTestHarness(unittest.TestCase):
00539 
00540     def setUp(self, *, is_package=True, **kwargs):
00541         self.package = 'pkg'
00542         if is_package:
00543             self.path = os.path.join(self.package, '__init__.py')
00544             self.name = self.package
00545         else:
00546             module_name = 'mod'
00547             self.path = os.path.join(self.package, '.'.join(['mod', 'py']))
00548             self.name = '.'.join([self.package, module_name])
00549         self.cached = imp.cache_from_source(self.path)
00550         self.loader = self.loader_mock(self.path, **kwargs)
00551 
00552     def verify_module(self, module):
00553         self.assertEqual(module.__name__, self.name)
00554         self.assertEqual(module.__file__, self.path)
00555         self.assertEqual(module.__cached__, self.cached)
00556         self.assertEqual(module.__package__, self.package)
00557         self.assertEqual(module.__loader__, self.loader)
00558         values = module._.split('::')
00559         self.assertEqual(values[0], self.name)
00560         self.assertEqual(values[1], self.path)
00561         self.assertEqual(values[2], self.cached)
00562         self.assertEqual(values[3], self.package)
00563         self.assertEqual(values[4], repr(self.loader))
00564 
00565     def verify_code(self, code_object):
00566         module = imp.new_module(self.name)
00567         module.__file__ = self.path
00568         module.__cached__ = self.cached
00569         module.__package__ = self.package
00570         module.__loader__ = self.loader
00571         module.__path__ = []
00572         exec(code_object, module.__dict__)
00573         self.verify_module(module)
00574 
00575 
00576 class SourceOnlyLoaderTests(SourceLoaderTestHarness):
00577 
00578     """Test importlib.abc.SourceLoader for source-only loading.
00579 
00580     Reload testing is subsumed by the tests for
00581     importlib.util.module_for_loader.
00582 
00583     """
00584 
00585     loader_mock = SourceOnlyLoaderMock
00586 
00587     def test_get_source(self):
00588         # Verify the source code is returned as a string.
00589         # If an IOError is raised by get_data then raise ImportError.
00590         expected_source = self.loader.source.decode('utf-8')
00591         self.assertEqual(self.loader.get_source(self.name), expected_source)
00592         def raise_IOError(path):
00593             raise IOError
00594         self.loader.get_data = raise_IOError
00595         with self.assertRaises(ImportError):
00596             self.loader.get_source(self.name)
00597 
00598     def test_is_package(self):
00599         # Properly detect when loading a package.
00600         self.setUp(is_package=True)
00601         self.assertTrue(self.loader.is_package(self.name))
00602         self.setUp(is_package=False)
00603         self.assertFalse(self.loader.is_package(self.name))
00604 
00605     def test_get_code(self):
00606         # Verify the code object is created.
00607         code_object = self.loader.get_code(self.name)
00608         self.verify_code(code_object)
00609 
00610     def test_load_module(self):
00611         # Loading a module should set __name__, __loader__, __package__,
00612         # __path__ (for packages), __file__, and __cached__.
00613         # The module should also be put into sys.modules.
00614         with util.uncache(self.name):
00615             module = self.loader.load_module(self.name)
00616             self.verify_module(module)
00617             self.assertEqual(module.__path__, [os.path.dirname(self.path)])
00618             self.assertTrue(self.name in sys.modules)
00619 
00620     def test_package_settings(self):
00621         # __package__ needs to be set, while __path__ is set on if the module
00622         # is a package.
00623         # Testing the values for a package are covered by test_load_module.
00624         self.setUp(is_package=False)
00625         with util.uncache(self.name):
00626             module = self.loader.load_module(self.name)
00627             self.verify_module(module)
00628             self.assertTrue(not hasattr(module, '__path__'))
00629 
00630     def test_get_source_encoding(self):
00631         # Source is considered encoded in UTF-8 by default unless otherwise
00632         # specified by an encoding line.
00633         source = "_ = 'ü'"
00634         self.loader.source = source.encode('utf-8')
00635         returned_source = self.loader.get_source(self.name)
00636         self.assertEqual(returned_source, source)
00637         source = "# coding: latin-1\n_ = ü"
00638         self.loader.source = source.encode('latin-1')
00639         returned_source = self.loader.get_source(self.name)
00640         self.assertEqual(returned_source, source)
00641 
00642 
00643 @unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true")
00644 class SourceLoaderBytecodeTests(SourceLoaderTestHarness):
00645 
00646     """Test importlib.abc.SourceLoader's use of bytecode.
00647 
00648     Source-only testing handled by SourceOnlyLoaderTests.
00649 
00650     """
00651 
00652     loader_mock = SourceLoaderMock
00653 
00654     def verify_code(self, code_object, *, bytecode_written=False):
00655         super().verify_code(code_object)
00656         if bytecode_written:
00657             self.assertIn(self.cached, self.loader.written)
00658             data = bytearray(imp.get_magic())
00659             data.extend(marshal._w_long(self.loader.source_mtime))
00660             data.extend(marshal.dumps(code_object))
00661             self.assertEqual(self.loader.written[self.cached], bytes(data))
00662 
00663     def test_code_with_everything(self):
00664         # When everything should work.
00665         code_object = self.loader.get_code(self.name)
00666         self.verify_code(code_object)
00667 
00668     def test_no_bytecode(self):
00669         # If no bytecode exists then move on to the source.
00670         self.loader.bytecode_path = "<does not exist>"
00671         # Sanity check
00672         with self.assertRaises(IOError):
00673             bytecode_path = imp.cache_from_source(self.path)
00674             self.loader.get_data(bytecode_path)
00675         code_object = self.loader.get_code(self.name)
00676         self.verify_code(code_object, bytecode_written=True)
00677 
00678     def test_code_bad_timestamp(self):
00679         # Bytecode is only used when the timestamp matches the source EXACTLY.
00680         for source_mtime in (0, 2):
00681             assert source_mtime != self.loader.source_mtime
00682             original = self.loader.source_mtime
00683             self.loader.source_mtime = source_mtime
00684             # If bytecode is used then EOFError would be raised by marshal.
00685             self.loader.bytecode = self.loader.bytecode[8:]
00686             code_object = self.loader.get_code(self.name)
00687             self.verify_code(code_object, bytecode_written=True)
00688             self.loader.source_mtime = original
00689 
00690     def test_code_bad_magic(self):
00691         # Skip over bytecode with a bad magic number.
00692         self.setUp(magic=b'0000')
00693         # If bytecode is used then EOFError would be raised by marshal.
00694         self.loader.bytecode = self.loader.bytecode[8:]
00695         code_object = self.loader.get_code(self.name)
00696         self.verify_code(code_object, bytecode_written=True)
00697 
00698     def test_dont_write_bytecode(self):
00699         # Bytecode is not written if sys.dont_write_bytecode is true.
00700         # Can assume it is false already thanks to the skipIf class decorator.
00701         try:
00702             sys.dont_write_bytecode = True
00703             self.loader.bytecode_path = "<does not exist>"
00704             code_object = self.loader.get_code(self.name)
00705             self.assertNotIn(self.cached, self.loader.written)
00706         finally:
00707             sys.dont_write_bytecode = False
00708 
00709     def test_no_set_data(self):
00710         # If set_data is not defined, one can still read bytecode.
00711         self.setUp(magic=b'0000')
00712         original_set_data = self.loader.__class__.set_data
00713         try:
00714             del self.loader.__class__.set_data
00715             code_object = self.loader.get_code(self.name)
00716             self.verify_code(code_object)
00717         finally:
00718             self.loader.__class__.set_data = original_set_data
00719 
00720     def test_set_data_raises_exceptions(self):
00721         # Raising NotImplementedError or IOError is okay for set_data.
00722         def raise_exception(exc):
00723             def closure(*args, **kwargs):
00724                 raise exc
00725             return closure
00726 
00727         self.setUp(magic=b'0000')
00728         self.loader.set_data = raise_exception(NotImplementedError)
00729         code_object = self.loader.get_code(self.name)
00730         self.verify_code(code_object)
00731 
00732 
00733 class SourceLoaderGetSourceTests(unittest.TestCase):
00734 
00735     """Tests for importlib.abc.SourceLoader.get_source()."""
00736 
00737     def test_default_encoding(self):
00738         # Should have no problems with UTF-8 text.
00739         name = 'mod'
00740         mock = SourceOnlyLoaderMock('mod.file')
00741         source = 'x = "ü"'
00742         mock.source = source.encode('utf-8')
00743         returned_source = mock.get_source(name)
00744         self.assertEqual(returned_source, source)
00745 
00746     def test_decoded_source(self):
00747         # Decoding should work.
00748         name = 'mod'
00749         mock = SourceOnlyLoaderMock("mod.file")
00750         source = "# coding: Latin-1\nx='ü'"
00751         assert source.encode('latin-1') != source.encode('utf-8')
00752         mock.source = source.encode('latin-1')
00753         returned_source = mock.get_source(name)
00754         self.assertEqual(returned_source, source)
00755 
00756     def test_universal_newlines(self):
00757         # PEP 302 says universal newlines should be used.
00758         name = 'mod'
00759         mock = SourceOnlyLoaderMock('mod.file')
00760         source = "x = 42\r\ny = -13\r\n"
00761         mock.source = source.encode('utf-8')
00762         expect = io.IncrementalNewlineDecoder(None, True).decode(source)
00763         self.assertEqual(mock.get_source(name), expect)
00764 
00765 class AbstractMethodImplTests(unittest.TestCase):
00766 
00767     """Test the concrete abstractmethod implementations."""
00768 
00769     class Loader(abc.Loader):
00770         def load_module(self, fullname):
00771             super().load_module(fullname)
00772 
00773     class Finder(abc.Finder):
00774         def find_module(self, _):
00775             super().find_module(_)
00776 
00777     class ResourceLoader(Loader, abc.ResourceLoader):
00778         def get_data(self, _):
00779             super().get_data(_)
00780 
00781     class InspectLoader(Loader, abc.InspectLoader):
00782         def is_package(self, _):
00783             super().is_package(_)
00784 
00785         def get_code(self, _):
00786             super().get_code(_)
00787 
00788         def get_source(self, _):
00789             super().get_source(_)
00790 
00791     class ExecutionLoader(InspectLoader, abc.ExecutionLoader):
00792         def get_filename(self, _):
00793             super().get_filename(_)
00794 
00795     class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader):
00796         pass
00797 
00798     class PyLoader(ResourceLoader, InspectLoader, abc.PyLoader):
00799         def source_path(self, _):
00800             super().source_path(_)
00801 
00802     class PyPycLoader(PyLoader, abc.PyPycLoader):
00803         def bytecode_path(self, _):
00804             super().bytecode_path(_)
00805 
00806         def source_mtime(self, _):
00807             super().source_mtime(_)
00808 
00809         def write_bytecode(self, _, _2):
00810             super().write_bytecode(_, _2)
00811 
00812     def raises_NotImplementedError(self, ins, *args):
00813         for method_name in args:
00814             method = getattr(ins, method_name)
00815             arg_count = len(inspect.getfullargspec(method)[0]) - 1
00816             args = [''] * arg_count
00817             try:
00818                 method(*args)
00819             except NotImplementedError:
00820                 pass
00821             else:
00822                 msg = "{}.{} did not raise NotImplementedError"
00823                 self.fail(msg.format(ins.__class__.__name__, method_name))
00824 
00825     def test_Loader(self):
00826         self.raises_NotImplementedError(self.Loader(), 'load_module')
00827 
00828     # XXX misplaced; should be somewhere else
00829     def test_Finder(self):
00830         self.raises_NotImplementedError(self.Finder(), 'find_module')
00831 
00832     def test_ResourceLoader(self):
00833         self.raises_NotImplementedError(self.ResourceLoader(), 'load_module',
00834                                         'get_data')
00835 
00836     def test_InspectLoader(self):
00837         self.raises_NotImplementedError(self.InspectLoader(), 'load_module',
00838                                         'is_package', 'get_code', 'get_source')
00839 
00840     def test_ExecutionLoader(self):
00841         self.raises_NotImplementedError(self.ExecutionLoader(), 'load_module',
00842                                         'is_package', 'get_code', 'get_source',
00843                                         'get_filename')
00844 
00845     def test_SourceLoader(self):
00846         ins = self.SourceLoader()
00847         # Required abstractmethods.
00848         self.raises_NotImplementedError(ins, 'get_filename', 'get_data')
00849         # Optional abstractmethods.
00850         self.raises_NotImplementedError(ins,'path_mtime', 'set_data')
00851 
00852     def test_PyLoader(self):
00853         self.raises_NotImplementedError(self.PyLoader(), 'source_path',
00854                                         'get_data', 'is_package')
00855 
00856     def test_PyPycLoader(self):
00857         self.raises_NotImplementedError(self.PyPycLoader(), 'source_path',
00858                                         'source_mtime', 'bytecode_path',
00859                                         'write_bytecode')
00860 
00861 
00862 def test_main():
00863     from test.support import run_unittest
00864     run_unittest(PyLoaderTests, PyLoaderCompatTests,
00865                     PyLoaderInterfaceTests,
00866                     PyPycLoaderTests, PyPycLoaderInterfaceTests,
00867                     SkipWritingBytecodeTests, RegeneratedBytecodeTests,
00868                     BadBytecodeFailureTests, MissingPathsTests,
00869                     SourceOnlyLoaderTests,
00870                     SourceLoaderBytecodeTests,
00871                     SourceLoaderGetSourceTests,
00872                     AbstractMethodImplTests)
00873 
00874 
00875 if __name__ == '__main__':
00876     test_main()