blob: 32459074a070fecf7c85bcbce639ed304e31bb61 [file] [log] [blame]
Brett Cannon2a922ed2009-03-09 03:35:50 +00001import importlib
2from importlib import abc
Brett Cannonf23e3742010-06-27 23:57:46 +00003
Brett Cannon2a922ed2009-03-09 03:35:50 +00004from .. import abc as testing_abc
5from .. import util
6from . import util as source_util
Brett Cannonf23e3742010-06-27 23:57:46 +00007
Brett Cannon2a922ed2009-03-09 03:35:50 +00008import imp
Brett Cannonf23e3742010-06-27 23:57:46 +00009import inspect
Brett Cannon418182e2010-07-03 22:32:41 +000010import io
Brett Cannon2a922ed2009-03-09 03:35:50 +000011import marshal
12import os
13import sys
14import types
15import unittest
Brett Cannonf23e3742010-06-27 23:57:46 +000016import warnings
17
18
19class SourceOnlyLoaderMock(abc.SourceLoader):
20
21 # Globals that should be defined for all modules.
22 source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, "
23 b"repr(__loader__)])")
24
25 def __init__(self, path):
26 self.path = path
27
28 def get_data(self, path):
29 assert self.path == path
30 return self.source
31
32 def get_filename(self, fullname):
33 return self.path
34
35
36class SourceLoaderMock(SourceOnlyLoaderMock):
37
38 source_mtime = 1
39
40 def __init__(self, path, magic=imp.get_magic()):
41 super().__init__(path)
42 self.bytecode_path = imp.cache_from_source(self.path)
43 data = bytearray(magic)
44 data.extend(marshal._w_long(self.source_mtime))
45 code_object = compile(self.source, self.path, 'exec',
46 dont_inherit=True)
47 data.extend(marshal.dumps(code_object))
48 self.bytecode = bytes(data)
49 self.written = {}
50
51 def get_data(self, path):
52 if path == self.path:
53 return super().get_data(path)
54 elif path == self.bytecode_path:
55 return self.bytecode
56 else:
57 raise IOError
58
59 def path_mtime(self, path):
60 assert path == self.path
61 return self.source_mtime
62
63 def set_data(self, path, data):
64 self.written[path] = bytes(data)
65 return path == self.bytecode_path
Brett Cannon2a922ed2009-03-09 03:35:50 +000066
67
68class PyLoaderMock(abc.PyLoader):
69
70 # Globals that should be defined for all modules.
Brett Cannond43b30b2009-03-10 03:29:23 +000071 source = (b"_ = '::'.join([__name__, __file__, __package__, "
72 b"repr(__loader__)])")
Brett Cannon2a922ed2009-03-09 03:35:50 +000073
74 def __init__(self, data):
75 """Take a dict of 'module_name: path' pairings.
76
77 Paths should have no file extension, allowing packages to be denoted by
78 ending in '__init__'.
79
80 """
81 self.module_paths = data
82 self.path_to_module = {val:key for key,val in data.items()}
83
84 def get_data(self, path):
85 if path not in self.path_to_module:
86 raise IOError
Brett Cannond43b30b2009-03-10 03:29:23 +000087 return self.source
Brett Cannon2a922ed2009-03-09 03:35:50 +000088
89 def is_package(self, name):
Brett Cannonf23e3742010-06-27 23:57:46 +000090 filename = os.path.basename(self.get_filename(name))
91 return os.path.splitext(filename)[0] == '__init__'
Brett Cannon2a922ed2009-03-09 03:35:50 +000092
Brett Cannon2a922ed2009-03-09 03:35:50 +000093 def source_path(self, name):
94 try:
95 return self.module_paths[name]
96 except KeyError:
97 raise ImportError
98
Brett Cannonf23e3742010-06-27 23:57:46 +000099 def get_filename(self, name):
100 """Silence deprecation warning."""
101 with warnings.catch_warnings(record=True) as w:
102 warnings.simplefilter("always")
103 path = super().get_filename(name)
104 assert len(w) == 1
105 assert issubclass(w[0].category, PendingDeprecationWarning)
106 return path
107
108
109class PyLoaderCompatMock(PyLoaderMock):
110
111 """Mock that matches what is suggested to have a loader that is compatible
112 from Python 3.1 onwards."""
113
114 def get_filename(self, fullname):
115 try:
116 return self.module_paths[fullname]
117 except KeyError:
118 raise ImportError
119
120 def source_path(self, fullname):
121 try:
122 return self.get_filename(fullname)
123 except ImportError:
124 return None
125
Brett Cannon2a922ed2009-03-09 03:35:50 +0000126
127class PyPycLoaderMock(abc.PyPycLoader, PyLoaderMock):
128
129 default_mtime = 1
130
131 def __init__(self, source, bc={}):
132 """Initialize mock.
133
134 'bc' is a dict keyed on a module's name. The value is dict with
135 possible keys of 'path', 'mtime', 'magic', and 'bc'. Except for 'path',
136 each of those keys control if any part of created bytecode is to
137 deviate from default values.
138
139 """
140 super().__init__(source)
141 self.module_bytecode = {}
142 self.path_to_bytecode = {}
143 self.bytecode_to_path = {}
144 for name, data in bc.items():
145 self.path_to_bytecode[data['path']] = name
146 self.bytecode_to_path[name] = data['path']
147 magic = data.get('magic', imp.get_magic())
148 mtime = importlib._w_long(data.get('mtime', self.default_mtime))
149 if 'bc' in data:
150 bc = data['bc']
151 else:
152 bc = self.compile_bc(name)
153 self.module_bytecode[name] = magic + mtime + bc
154
155 def compile_bc(self, name):
156 source_path = self.module_paths.get(name, '<test>') or '<test>'
157 code = compile(self.source, source_path, 'exec')
158 return marshal.dumps(code)
159
160 def source_mtime(self, name):
161 if name in self.module_paths:
162 return self.default_mtime
163 elif name in self.module_bytecode:
164 return None
165 else:
166 raise ImportError
167
168 def bytecode_path(self, name):
169 try:
170 return self.bytecode_to_path[name]
171 except KeyError:
172 if name in self.module_paths:
173 return None
174 else:
175 raise ImportError
176
177 def write_bytecode(self, name, bytecode):
178 self.module_bytecode[name] = bytecode
179 return True
180
181 def get_data(self, path):
182 if path in self.path_to_module:
183 return super().get_data(path)
184 elif path in self.path_to_bytecode:
185 name = self.path_to_bytecode[path]
186 return self.module_bytecode[name]
187 else:
188 raise IOError
189
190 def is_package(self, name):
191 try:
192 return super().is_package(name)
193 except TypeError:
194 return '__init__' in self.bytecode_to_path[name]
195
Brett Cannonf23e3742010-06-27 23:57:46 +0000196 def get_code(self, name):
197 with warnings.catch_warnings(record=True) as w:
198 warnings.simplefilter("always")
199 code_object = super().get_code(name)
200 assert len(w) == 1
201 assert issubclass(w[0].category, PendingDeprecationWarning)
202 return code_object
Brett Cannon2a922ed2009-03-09 03:35:50 +0000203
204class PyLoaderTests(testing_abc.LoaderTests):
205
206 """Tests for importlib.abc.PyLoader."""
207
208 mocker = PyLoaderMock
209
210 def eq_attrs(self, ob, **kwargs):
211 for attr, val in kwargs.items():
Brett Cannon978259e2009-04-02 15:32:07 +0000212 found = getattr(ob, attr)
213 self.assertEqual(found, val,
214 "{} attribute: {} != {}".format(attr, found, val))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000215
216 def test_module(self):
217 name = '<module>'
Brett Cannon55619822009-04-02 17:54:43 +0000218 path = os.path.join('', 'path', 'to', 'module')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000219 mock = self.mocker({name: path})
220 with util.uncache(name):
221 module = mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000222 self.assertTrue(name in sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000223 self.eq_attrs(module, __name__=name, __file__=path, __package__='',
224 __loader__=mock)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000225 self.assertTrue(not hasattr(module, '__path__'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000226 return mock, name
227
228 def test_package(self):
229 name = '<pkg>'
Brett Cannon55619822009-04-02 17:54:43 +0000230 path = os.path.join('path', 'to', name, '__init__')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000231 mock = self.mocker({name: path})
232 with util.uncache(name):
233 module = mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000234 self.assertTrue(name in sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000235 self.eq_attrs(module, __name__=name, __file__=path,
236 __path__=[os.path.dirname(path)], __package__=name,
237 __loader__=mock)
238 return mock, name
239
240 def test_lacking_parent(self):
241 name = 'pkg.mod'
Brett Cannon55619822009-04-02 17:54:43 +0000242 path = os.path.join('path', 'to', 'pkg', 'mod')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000243 mock = self.mocker({name: path})
244 with util.uncache(name):
245 module = mock.load_module(name)
Brett Cannon1262e7c2009-05-11 01:47:11 +0000246 self.assertIn(name, sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000247 self.eq_attrs(module, __name__=name, __file__=path, __package__='pkg',
248 __loader__=mock)
Brett Cannon1262e7c2009-05-11 01:47:11 +0000249 self.assertFalse(hasattr(module, '__path__'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000250 return mock, name
251
252 def test_module_reuse(self):
253 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000254 path = os.path.join('path', 'to', 'mod')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000255 module = imp.new_module(name)
256 mock = self.mocker({name: path})
257 with util.uncache(name):
258 sys.modules[name] = module
259 loaded_module = mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000260 self.assertTrue(loaded_module is module)
261 self.assertTrue(sys.modules[name] is module)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000262 return mock, name
263
264 def test_state_after_failure(self):
265 name = "mod"
266 module = imp.new_module(name)
267 module.blah = None
Brett Cannon55619822009-04-02 17:54:43 +0000268 mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
Brett Cannond43b30b2009-03-10 03:29:23 +0000269 mock.source = b"1/0"
Brett Cannon2a922ed2009-03-09 03:35:50 +0000270 with util.uncache(name):
271 sys.modules[name] = module
Brett Cannon0a49c582009-07-19 23:43:45 +0000272 with self.assertRaises(ZeroDivisionError):
273 mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000274 self.assertTrue(sys.modules[name] is module)
275 self.assertTrue(hasattr(module, 'blah'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000276 return mock
277
278 def test_unloadable(self):
279 name = "mod"
Brett Cannon55619822009-04-02 17:54:43 +0000280 mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
Brett Cannond43b30b2009-03-10 03:29:23 +0000281 mock.source = b"1/0"
Brett Cannon2a922ed2009-03-09 03:35:50 +0000282 with util.uncache(name):
Brett Cannon0a49c582009-07-19 23:43:45 +0000283 with self.assertRaises(ZeroDivisionError):
284 mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000285 self.assertTrue(name not in sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000286 return mock
287
288
Brett Cannonf23e3742010-06-27 23:57:46 +0000289class PyLoaderCompatTests(PyLoaderTests):
290
291 """Test that the suggested code to make a loader that is compatible from
292 Python 3.1 forward works."""
293
294 mocker = PyLoaderCompatMock
295
296
Brett Cannon2a922ed2009-03-09 03:35:50 +0000297class PyLoaderInterfaceTests(unittest.TestCase):
298
Brett Cannond43b30b2009-03-10 03:29:23 +0000299 """Tests for importlib.abc.PyLoader to make sure that when source_path()
300 doesn't return a path everything works as expected."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000301
302 def test_no_source_path(self):
303 # No source path should lead to ImportError.
304 name = 'mod'
305 mock = PyLoaderMock({})
Brett Cannon0a49c582009-07-19 23:43:45 +0000306 with util.uncache(name), self.assertRaises(ImportError):
307 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000308
309 def test_source_path_is_None(self):
310 name = 'mod'
311 mock = PyLoaderMock({name: None})
Brett Cannon0a49c582009-07-19 23:43:45 +0000312 with util.uncache(name), self.assertRaises(ImportError):
313 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000314
Brett Cannon69194272009-07-20 04:23:48 +0000315 def test_get_filename_with_source_path(self):
316 # get_filename() should return what source_path() returns.
317 name = 'mod'
318 path = os.path.join('path', 'to', 'source')
319 mock = PyLoaderMock({name: path})
320 with util.uncache(name):
321 self.assertEqual(mock.get_filename(name), path)
322
323 def test_get_filename_no_source_path(self):
324 # get_filename() should raise ImportError if source_path returns None.
325 name = 'mod'
326 mock = PyLoaderMock({name: None})
327 with util.uncache(name), self.assertRaises(ImportError):
328 mock.get_filename(name)
329
Brett Cannon2a922ed2009-03-09 03:35:50 +0000330
331class PyPycLoaderTests(PyLoaderTests):
332
333 """Tests for importlib.abc.PyPycLoader."""
334
335 mocker = PyPycLoaderMock
336
Brett Cannon1262e7c2009-05-11 01:47:11 +0000337 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000338 def verify_bytecode(self, mock, name):
339 assert name in mock.module_paths
Brett Cannon1262e7c2009-05-11 01:47:11 +0000340 self.assertIn(name, mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000341 magic = mock.module_bytecode[name][:4]
342 self.assertEqual(magic, imp.get_magic())
343 mtime = importlib._r_long(mock.module_bytecode[name][4:8])
344 self.assertEqual(mtime, 1)
345 bc = mock.module_bytecode[name][8:]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000346 self.assertEqual(bc, mock.compile_bc(name))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000347
348 def test_module(self):
349 mock, name = super().test_module()
350 self.verify_bytecode(mock, name)
351
352 def test_package(self):
353 mock, name = super().test_package()
354 self.verify_bytecode(mock, name)
355
356 def test_lacking_parent(self):
357 mock, name = super().test_lacking_parent()
358 self.verify_bytecode(mock, name)
359
360 def test_module_reuse(self):
361 mock, name = super().test_module_reuse()
362 self.verify_bytecode(mock, name)
363
364 def test_state_after_failure(self):
365 super().test_state_after_failure()
366
367 def test_unloadable(self):
368 super().test_unloadable()
369
370
Brett Cannon69194272009-07-20 04:23:48 +0000371class PyPycLoaderInterfaceTests(unittest.TestCase):
372
373 """Test for the interface of importlib.abc.PyPycLoader."""
374
375 def get_filename_check(self, src_path, bc_path, expect):
376 name = 'mod'
377 mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}})
378 with util.uncache(name):
379 assert mock.source_path(name) == src_path
380 assert mock.bytecode_path(name) == bc_path
381 self.assertEqual(mock.get_filename(name), expect)
382
383 def test_filename_with_source_bc(self):
384 # When source and bytecode paths present, return the source path.
385 self.get_filename_check('source_path', 'bc_path', 'source_path')
386
387 def test_filename_with_source_no_bc(self):
388 # With source but no bc, return source path.
389 self.get_filename_check('source_path', None, 'source_path')
390
391 def test_filename_with_no_source_bc(self):
392 # With not source but bc, return the bc path.
393 self.get_filename_check(None, 'bc_path', 'bc_path')
394
395 def test_filename_with_no_source_or_bc(self):
396 # With no source or bc, raise ImportError.
397 name = 'mod'
398 mock = PyPycLoaderMock({name: None}, {name: {'path': None}})
399 with util.uncache(name), self.assertRaises(ImportError):
400 mock.get_filename(name)
401
402
Brett Cannon2a922ed2009-03-09 03:35:50 +0000403class SkipWritingBytecodeTests(unittest.TestCase):
404
405 """Test that bytecode is properly handled based on
406 sys.dont_write_bytecode."""
407
Brett Cannon1262e7c2009-05-11 01:47:11 +0000408 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000409 def run_test(self, dont_write_bytecode):
410 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000411 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
Brett Cannon2a922ed2009-03-09 03:35:50 +0000412 sys.dont_write_bytecode = dont_write_bytecode
413 with util.uncache(name):
414 mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000415 self.assertTrue((name in mock.module_bytecode) is not
Brett Cannon2a922ed2009-03-09 03:35:50 +0000416 dont_write_bytecode)
417
418 def test_no_bytecode_written(self):
419 self.run_test(True)
420
421 def test_bytecode_written(self):
422 self.run_test(False)
423
424
425class RegeneratedBytecodeTests(unittest.TestCase):
426
427 """Test that bytecode is regenerated as expected."""
428
Brett Cannon1262e7c2009-05-11 01:47:11 +0000429 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000430 def test_different_magic(self):
431 # A different magic number should lead to new bytecode.
432 name = 'mod'
433 bad_magic = b'\x00\x00\x00\x00'
434 assert bad_magic != imp.get_magic()
Brett Cannon55619822009-04-02 17:54:43 +0000435 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
436 {name: {'path': os.path.join('path', 'to',
437 'mod.bytecode'),
Brett Cannon2a922ed2009-03-09 03:35:50 +0000438 'magic': bad_magic}})
439 with util.uncache(name):
440 mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000441 self.assertTrue(name in mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000442 magic = mock.module_bytecode[name][:4]
443 self.assertEqual(magic, imp.get_magic())
444
Brett Cannon1262e7c2009-05-11 01:47:11 +0000445 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000446 def test_old_mtime(self):
447 # Bytecode with an older mtime should be regenerated.
448 name = 'mod'
449 old_mtime = PyPycLoaderMock.default_mtime - 1
Brett Cannon55619822009-04-02 17:54:43 +0000450 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
Brett Cannon2a922ed2009-03-09 03:35:50 +0000451 {name: {'path': 'path/to/mod.bytecode', 'mtime': old_mtime}})
452 with util.uncache(name):
453 mock.load_module(name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000454 self.assertTrue(name in mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000455 mtime = importlib._r_long(mock.module_bytecode[name][4:8])
456 self.assertEqual(mtime, PyPycLoaderMock.default_mtime)
457
458
459class BadBytecodeFailureTests(unittest.TestCase):
460
461 """Test import failures when there is no source and parts of the bytecode
462 is bad."""
463
464 def test_bad_magic(self):
465 # A bad magic number should lead to an ImportError.
466 name = 'mod'
467 bad_magic = b'\x00\x00\x00\x00'
Brett Cannon3c273842009-07-20 00:14:29 +0000468 bc = {name:
469 {'path': os.path.join('path', 'to', 'mod'),
470 'magic': bad_magic}}
471 mock = PyPycLoaderMock({name: None}, bc)
Brett Cannon0a49c582009-07-19 23:43:45 +0000472 with util.uncache(name), self.assertRaises(ImportError):
473 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000474
Brett Cannon3c273842009-07-20 00:14:29 +0000475 def test_no_bytecode(self):
476 # Missing code object bytecode should lead to an EOFError.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000477 name = 'mod'
Brett Cannon3c273842009-07-20 00:14:29 +0000478 bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b''}}
479 mock = PyPycLoaderMock({name: None}, bc)
480 with util.uncache(name), self.assertRaises(EOFError):
481 mock.load_module(name)
482
483 def test_bad_bytecode(self):
484 # Malformed code object bytecode should lead to a ValueError.
485 name = 'mod'
Benjamin Peterson2215c142010-06-28 00:24:13 +0000486 bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b'1234'}}
Brett Cannon3c273842009-07-20 00:14:29 +0000487 mock = PyPycLoaderMock({name: None}, bc)
488 with util.uncache(name), self.assertRaises(ValueError):
Brett Cannon0a49c582009-07-19 23:43:45 +0000489 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000490
491
492def raise_ImportError(*args, **kwargs):
493 raise ImportError
494
495class MissingPathsTests(unittest.TestCase):
496
497 """Test what happens when a source or bytecode path does not exist (either
498 from *_path returning None or raising ImportError)."""
499
500 def test_source_path_None(self):
501 # Bytecode should be used when source_path returns None, along with
502 # __file__ being set to the bytecode path.
503 name = 'mod'
504 bytecode_path = 'path/to/mod'
505 mock = PyPycLoaderMock({name: None}, {name: {'path': bytecode_path}})
506 with util.uncache(name):
507 module = mock.load_module(name)
508 self.assertEqual(module.__file__, bytecode_path)
509
510 # Testing for bytecode_path returning None handled by all tests where no
511 # bytecode initially exists.
512
513 def test_all_paths_None(self):
514 # If all *_path methods return None, raise ImportError.
515 name = 'mod'
516 mock = PyPycLoaderMock({name: None})
Brett Cannon0a49c582009-07-19 23:43:45 +0000517 with util.uncache(name), self.assertRaises(ImportError):
518 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000519
520 def test_source_path_ImportError(self):
521 # An ImportError from source_path should trigger an ImportError.
522 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000523 mock = PyPycLoaderMock({}, {name: {'path': os.path.join('path', 'to',
524 'mod')}})
Brett Cannon0a49c582009-07-19 23:43:45 +0000525 with util.uncache(name), self.assertRaises(ImportError):
526 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000527
528 def test_bytecode_path_ImportError(self):
529 # An ImportError from bytecode_path should trigger an ImportError.
530 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000531 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
Brett Cannon2a922ed2009-03-09 03:35:50 +0000532 bad_meth = types.MethodType(raise_ImportError, mock)
533 mock.bytecode_path = bad_meth
Brett Cannon0a49c582009-07-19 23:43:45 +0000534 with util.uncache(name), self.assertRaises(ImportError):
535 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000536
537
Brett Cannonf23e3742010-06-27 23:57:46 +0000538class SourceLoaderTestHarness(unittest.TestCase):
539
540 def setUp(self, *, is_package=True, **kwargs):
541 self.package = 'pkg'
542 if is_package:
543 self.path = os.path.join(self.package, '__init__.py')
544 self.name = self.package
545 else:
546 module_name = 'mod'
547 self.path = os.path.join(self.package, '.'.join(['mod', 'py']))
548 self.name = '.'.join([self.package, module_name])
549 self.cached = imp.cache_from_source(self.path)
550 self.loader = self.loader_mock(self.path, **kwargs)
551
552 def verify_module(self, module):
553 self.assertEqual(module.__name__, self.name)
554 self.assertEqual(module.__file__, self.path)
555 self.assertEqual(module.__cached__, self.cached)
556 self.assertEqual(module.__package__, self.package)
557 self.assertEqual(module.__loader__, self.loader)
558 values = module._.split('::')
559 self.assertEqual(values[0], self.name)
560 self.assertEqual(values[1], self.path)
561 self.assertEqual(values[2], self.cached)
562 self.assertEqual(values[3], self.package)
563 self.assertEqual(values[4], repr(self.loader))
564
565 def verify_code(self, code_object):
566 module = imp.new_module(self.name)
567 module.__file__ = self.path
568 module.__cached__ = self.cached
569 module.__package__ = self.package
570 module.__loader__ = self.loader
571 module.__path__ = []
572 exec(code_object, module.__dict__)
573 self.verify_module(module)
574
575
576class SourceOnlyLoaderTests(SourceLoaderTestHarness):
577
578 """Test importlib.abc.SourceLoader for source-only loading.
579
580 Reload testing is subsumed by the tests for
581 importlib.util.module_for_loader.
582
583 """
584
585 loader_mock = SourceOnlyLoaderMock
586
587 def test_get_source(self):
588 # Verify the source code is returned as a string.
589 # If an IOError is raised by get_data then raise ImportError.
590 expected_source = self.loader.source.decode('utf-8')
591 self.assertEqual(self.loader.get_source(self.name), expected_source)
592 def raise_IOError(path):
593 raise IOError
594 self.loader.get_data = raise_IOError
595 with self.assertRaises(ImportError):
596 self.loader.get_source(self.name)
597
598 def test_is_package(self):
599 # Properly detect when loading a package.
600 self.setUp(is_package=True)
601 self.assertTrue(self.loader.is_package(self.name))
602 self.setUp(is_package=False)
603 self.assertFalse(self.loader.is_package(self.name))
604
605 def test_get_code(self):
606 # Verify the code object is created.
607 code_object = self.loader.get_code(self.name)
608 self.verify_code(code_object)
609
610 def test_load_module(self):
611 # Loading a module should set __name__, __loader__, __package__,
612 # __path__ (for packages), __file__, and __cached__.
613 # The module should also be put into sys.modules.
614 with util.uncache(self.name):
615 module = self.loader.load_module(self.name)
616 self.verify_module(module)
617 self.assertEqual(module.__path__, [os.path.dirname(self.path)])
618 self.assertTrue(self.name in sys.modules)
619
620 def test_package_settings(self):
621 # __package__ needs to be set, while __path__ is set on if the module
622 # is a package.
623 # Testing the values for a package are covered by test_load_module.
624 self.setUp(is_package=False)
625 with util.uncache(self.name):
626 module = self.loader.load_module(self.name)
627 self.verify_module(module)
628 self.assertTrue(not hasattr(module, '__path__'))
629
630 def test_get_source_encoding(self):
631 # Source is considered encoded in UTF-8 by default unless otherwise
632 # specified by an encoding line.
633 source = "_ = 'ü'"
634 self.loader.source = source.encode('utf-8')
635 returned_source = self.loader.get_source(self.name)
636 self.assertEqual(returned_source, source)
637 source = "# coding: latin-1\n_ = ü"
638 self.loader.source = source.encode('latin-1')
639 returned_source = self.loader.get_source(self.name)
640 self.assertEqual(returned_source, source)
641
642
643@unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true")
644class SourceLoaderBytecodeTests(SourceLoaderTestHarness):
645
646 """Test importlib.abc.SourceLoader's use of bytecode.
647
648 Source-only testing handled by SourceOnlyLoaderTests.
649
650 """
651
652 loader_mock = SourceLoaderMock
653
654 def verify_code(self, code_object, *, bytecode_written=False):
655 super().verify_code(code_object)
656 if bytecode_written:
657 self.assertIn(self.cached, self.loader.written)
658 data = bytearray(imp.get_magic())
659 data.extend(marshal._w_long(self.loader.source_mtime))
660 data.extend(marshal.dumps(code_object))
661 self.assertEqual(self.loader.written[self.cached], bytes(data))
662
663 def test_code_with_everything(self):
664 # When everything should work.
665 code_object = self.loader.get_code(self.name)
666 self.verify_code(code_object)
667
668 def test_no_bytecode(self):
669 # If no bytecode exists then move on to the source.
670 self.loader.bytecode_path = "<does not exist>"
671 # Sanity check
672 with self.assertRaises(IOError):
673 bytecode_path = imp.cache_from_source(self.path)
674 self.loader.get_data(bytecode_path)
675 code_object = self.loader.get_code(self.name)
676 self.verify_code(code_object, bytecode_written=True)
677
678 def test_code_bad_timestamp(self):
679 # Bytecode is only used when the timestamp matches the source EXACTLY.
680 for source_mtime in (0, 2):
681 assert source_mtime != self.loader.source_mtime
682 original = self.loader.source_mtime
683 self.loader.source_mtime = source_mtime
684 # If bytecode is used then EOFError would be raised by marshal.
685 self.loader.bytecode = self.loader.bytecode[8:]
686 code_object = self.loader.get_code(self.name)
687 self.verify_code(code_object, bytecode_written=True)
688 self.loader.source_mtime = original
689
690 def test_code_bad_magic(self):
691 # Skip over bytecode with a bad magic number.
692 self.setUp(magic=b'0000')
693 # If bytecode is used then EOFError would be raised by marshal.
694 self.loader.bytecode = self.loader.bytecode[8:]
695 code_object = self.loader.get_code(self.name)
696 self.verify_code(code_object, bytecode_written=True)
697
698 def test_dont_write_bytecode(self):
699 # Bytecode is not written if sys.dont_write_bytecode is true.
700 # Can assume it is false already thanks to the skipIf class decorator.
701 try:
702 sys.dont_write_bytecode = True
703 self.loader.bytecode_path = "<does not exist>"
704 code_object = self.loader.get_code(self.name)
705 self.assertNotIn(self.cached, self.loader.written)
706 finally:
707 sys.dont_write_bytecode = False
708
709 def test_no_set_data(self):
710 # If set_data is not defined, one can still read bytecode.
711 self.setUp(magic=b'0000')
712 original_set_data = self.loader.__class__.set_data
713 try:
714 del self.loader.__class__.set_data
715 code_object = self.loader.get_code(self.name)
716 self.verify_code(code_object)
717 finally:
718 self.loader.__class__.set_data = original_set_data
719
720 def test_set_data_raises_exceptions(self):
721 # Raising NotImplementedError or IOError is okay for set_data.
722 def raise_exception(exc):
723 def closure(*args, **kwargs):
724 raise exc
725 return closure
726
727 self.setUp(magic=b'0000')
Brett Cannon0cf9e6a2010-06-28 04:57:24 +0000728 self.loader.set_data = raise_exception(NotImplementedError)
729 code_object = self.loader.get_code(self.name)
730 self.verify_code(code_object)
Brett Cannonf23e3742010-06-27 23:57:46 +0000731
Brett Cannon418182e2010-07-03 22:32:41 +0000732
733class SourceLoaderGetSourceTests(unittest.TestCase):
734
735 """Tests for importlib.abc.SourceLoader.get_source()."""
736
737 def test_default_encoding(self):
738 # Should have no problems with UTF-8 text.
739 name = 'mod'
740 mock = SourceOnlyLoaderMock('mod.file')
741 source = 'x = "ü"'
742 mock.source = source.encode('utf-8')
743 returned_source = mock.get_source(name)
744 self.assertEqual(returned_source, source)
745
746 def test_decoded_source(self):
747 # Decoding should work.
748 name = 'mod'
749 mock = SourceOnlyLoaderMock("mod.file")
750 source = "# coding: Latin-1\nx='ü'"
751 assert source.encode('latin-1') != source.encode('utf-8')
752 mock.source = source.encode('latin-1')
753 returned_source = mock.get_source(name)
754 self.assertEqual(returned_source, source)
755
756 def test_universal_newlines(self):
757 # PEP 302 says universal newlines should be used.
758 name = 'mod'
759 mock = SourceOnlyLoaderMock('mod.file')
760 source = "x = 42\r\ny = -13\r\n"
761 mock.source = source.encode('utf-8')
762 expect = io.IncrementalNewlineDecoder(None, True).decode(source)
763 self.assertEqual(mock.get_source(name), expect)
764
Brett Cannonf23e3742010-06-27 23:57:46 +0000765class AbstractMethodImplTests(unittest.TestCase):
766
767 """Test the concrete abstractmethod implementations."""
768
769 class Loader(abc.Loader):
770 def load_module(self, fullname):
771 super().load_module(fullname)
772
773 class Finder(abc.Finder):
774 def find_module(self, _):
775 super().find_module(_)
776
777 class ResourceLoader(Loader, abc.ResourceLoader):
778 def get_data(self, _):
779 super().get_data(_)
780
781 class InspectLoader(Loader, abc.InspectLoader):
782 def is_package(self, _):
783 super().is_package(_)
784
785 def get_code(self, _):
786 super().get_code(_)
787
788 def get_source(self, _):
789 super().get_source(_)
790
791 class ExecutionLoader(InspectLoader, abc.ExecutionLoader):
792 def get_filename(self, _):
793 super().get_filename(_)
794
795 class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader):
796 pass
797
798 class PyLoader(ResourceLoader, InspectLoader, abc.PyLoader):
799 def source_path(self, _):
800 super().source_path(_)
801
802 class PyPycLoader(PyLoader, abc.PyPycLoader):
803 def bytecode_path(self, _):
804 super().bytecode_path(_)
805
806 def source_mtime(self, _):
807 super().source_mtime(_)
808
809 def write_bytecode(self, _, _2):
810 super().write_bytecode(_, _2)
811
812 def raises_NotImplementedError(self, ins, *args):
813 for method_name in args:
814 method = getattr(ins, method_name)
815 arg_count = len(inspect.getfullargspec(method)[0]) - 1
816 args = [''] * arg_count
817 try:
818 method(*args)
819 except NotImplementedError:
820 pass
821 else:
822 msg = "{}.{} did not raise NotImplementedError"
823 self.fail(msg.format(ins.__class__.__name__, method_name))
824
825 def test_Loader(self):
826 self.raises_NotImplementedError(self.Loader(), 'load_module')
827
Brett Cannon61b14252010-07-03 21:48:25 +0000828 # XXX misplaced; should be somewhere else
Brett Cannonf23e3742010-06-27 23:57:46 +0000829 def test_Finder(self):
830 self.raises_NotImplementedError(self.Finder(), 'find_module')
831
832 def test_ResourceLoader(self):
833 self.raises_NotImplementedError(self.ResourceLoader(), 'load_module',
834 'get_data')
835
836 def test_InspectLoader(self):
837 self.raises_NotImplementedError(self.InspectLoader(), 'load_module',
838 'is_package', 'get_code', 'get_source')
839
840 def test_ExecutionLoader(self):
841 self.raises_NotImplementedError(self.ExecutionLoader(), 'load_module',
842 'is_package', 'get_code', 'get_source',
843 'get_filename')
844
845 def test_SourceLoader(self):
846 ins = self.SourceLoader()
847 # Required abstractmethods.
848 self.raises_NotImplementedError(ins, 'get_filename', 'get_data')
849 # Optional abstractmethods.
850 self.raises_NotImplementedError(ins,'path_mtime', 'set_data')
851
852 def test_PyLoader(self):
853 self.raises_NotImplementedError(self.PyLoader(), 'source_path',
854 'get_data', 'is_package')
855
856 def test_PyPycLoader(self):
857 self.raises_NotImplementedError(self.PyPycLoader(), 'source_path',
858 'source_mtime', 'bytecode_path',
859 'write_bytecode')
860
861
Brett Cannon2a922ed2009-03-09 03:35:50 +0000862def test_main():
863 from test.support import run_unittest
Brett Cannonf23e3742010-06-27 23:57:46 +0000864 run_unittest(PyLoaderTests, PyLoaderCompatTests,
Brett Cannon418182e2010-07-03 22:32:41 +0000865 PyLoaderInterfaceTests,
Brett Cannon69194272009-07-20 04:23:48 +0000866 PyPycLoaderTests, PyPycLoaderInterfaceTests,
867 SkipWritingBytecodeTests, RegeneratedBytecodeTests,
Brett Cannonf23e3742010-06-27 23:57:46 +0000868 BadBytecodeFailureTests, MissingPathsTests,
869 SourceOnlyLoaderTests,
870 SourceLoaderBytecodeTests,
Brett Cannon418182e2010-07-03 22:32:41 +0000871 SourceLoaderGetSourceTests,
Brett Cannonf23e3742010-06-27 23:57:46 +0000872 AbstractMethodImplTests)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000873
874
875if __name__ == '__main__':
876 test_main()