blob: 0d912b64692a022b4e85f517bda2023367f951a8 [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
Barry Warsaw0efcf992012-07-31 17:52:32 -040035 def module_repr(self, module):
36 return '<module>'
37
Brett Cannonf23e3742010-06-27 23:57:46 +000038
39class SourceLoaderMock(SourceOnlyLoaderMock):
40
41 source_mtime = 1
42
43 def __init__(self, path, magic=imp.get_magic()):
44 super().__init__(path)
45 self.bytecode_path = imp.cache_from_source(self.path)
Antoine Pitrou5136ac02012-01-13 18:52:16 +010046 self.source_size = len(self.source)
Brett Cannonf23e3742010-06-27 23:57:46 +000047 data = bytearray(magic)
Brett Cannonc264e3e2012-01-25 18:58:03 -050048 data.extend(importlib._w_long(self.source_mtime))
49 data.extend(importlib._w_long(self.source_size))
Brett Cannonf23e3742010-06-27 23:57:46 +000050 code_object = compile(self.source, self.path, 'exec',
51 dont_inherit=True)
52 data.extend(marshal.dumps(code_object))
53 self.bytecode = bytes(data)
54 self.written = {}
55
56 def get_data(self, path):
57 if path == self.path:
58 return super().get_data(path)
59 elif path == self.bytecode_path:
60 return self.bytecode
61 else:
62 raise IOError
63
Antoine Pitrou5136ac02012-01-13 18:52:16 +010064 def path_stats(self, path):
Brett Cannonf23e3742010-06-27 23:57:46 +000065 assert path == self.path
Antoine Pitrou5136ac02012-01-13 18:52:16 +010066 return {'mtime': self.source_mtime, 'size': self.source_size}
Brett Cannonf23e3742010-06-27 23:57:46 +000067
68 def set_data(self, path, data):
69 self.written[path] = bytes(data)
70 return path == self.bytecode_path
Brett Cannon2a922ed2009-03-09 03:35:50 +000071
72
73class PyLoaderMock(abc.PyLoader):
74
75 # Globals that should be defined for all modules.
Brett Cannond43b30b2009-03-10 03:29:23 +000076 source = (b"_ = '::'.join([__name__, __file__, __package__, "
77 b"repr(__loader__)])")
Brett Cannon2a922ed2009-03-09 03:35:50 +000078
79 def __init__(self, data):
80 """Take a dict of 'module_name: path' pairings.
81
82 Paths should have no file extension, allowing packages to be denoted by
83 ending in '__init__'.
84
85 """
86 self.module_paths = data
87 self.path_to_module = {val:key for key,val in data.items()}
88
89 def get_data(self, path):
90 if path not in self.path_to_module:
91 raise IOError
Brett Cannond43b30b2009-03-10 03:29:23 +000092 return self.source
Brett Cannon2a922ed2009-03-09 03:35:50 +000093
94 def is_package(self, name):
Brett Cannonf23e3742010-06-27 23:57:46 +000095 filename = os.path.basename(self.get_filename(name))
96 return os.path.splitext(filename)[0] == '__init__'
Brett Cannon2a922ed2009-03-09 03:35:50 +000097
Brett Cannon2a922ed2009-03-09 03:35:50 +000098 def source_path(self, name):
99 try:
100 return self.module_paths[name]
101 except KeyError:
102 raise ImportError
103
Brett Cannonf23e3742010-06-27 23:57:46 +0000104 def get_filename(self, name):
105 """Silence deprecation warning."""
106 with warnings.catch_warnings(record=True) as w:
107 warnings.simplefilter("always")
108 path = super().get_filename(name)
109 assert len(w) == 1
Florent Xicluna67317752011-12-10 11:07:42 +0100110 assert issubclass(w[0].category, DeprecationWarning)
Brett Cannonf23e3742010-06-27 23:57:46 +0000111 return path
112
Barry Warsaw0efcf992012-07-31 17:52:32 -0400113 def module_repr(self):
114 return '<module>'
115
Brett Cannonf23e3742010-06-27 23:57:46 +0000116
117class PyLoaderCompatMock(PyLoaderMock):
118
119 """Mock that matches what is suggested to have a loader that is compatible
120 from Python 3.1 onwards."""
121
122 def get_filename(self, fullname):
123 try:
124 return self.module_paths[fullname]
125 except KeyError:
126 raise ImportError
127
128 def source_path(self, fullname):
129 try:
130 return self.get_filename(fullname)
131 except ImportError:
132 return None
133
Brett Cannon2a922ed2009-03-09 03:35:50 +0000134
135class PyPycLoaderMock(abc.PyPycLoader, PyLoaderMock):
136
137 default_mtime = 1
138
139 def __init__(self, source, bc={}):
140 """Initialize mock.
141
142 'bc' is a dict keyed on a module's name. The value is dict with
143 possible keys of 'path', 'mtime', 'magic', and 'bc'. Except for 'path',
144 each of those keys control if any part of created bytecode is to
145 deviate from default values.
146
147 """
148 super().__init__(source)
149 self.module_bytecode = {}
150 self.path_to_bytecode = {}
151 self.bytecode_to_path = {}
152 for name, data in bc.items():
153 self.path_to_bytecode[data['path']] = name
154 self.bytecode_to_path[name] = data['path']
155 magic = data.get('magic', imp.get_magic())
156 mtime = importlib._w_long(data.get('mtime', self.default_mtime))
Brett Cannon1e331562012-07-02 14:35:34 -0400157 source_size = importlib._w_long(len(self.source) & 0xFFFFFFFF)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000158 if 'bc' in data:
159 bc = data['bc']
160 else:
161 bc = self.compile_bc(name)
Brett Cannon1e331562012-07-02 14:35:34 -0400162 self.module_bytecode[name] = magic + mtime + source_size + bc
Brett Cannon2a922ed2009-03-09 03:35:50 +0000163
164 def compile_bc(self, name):
165 source_path = self.module_paths.get(name, '<test>') or '<test>'
166 code = compile(self.source, source_path, 'exec')
167 return marshal.dumps(code)
168
169 def source_mtime(self, name):
170 if name in self.module_paths:
171 return self.default_mtime
172 elif name in self.module_bytecode:
173 return None
174 else:
175 raise ImportError
176
177 def bytecode_path(self, name):
178 try:
179 return self.bytecode_to_path[name]
180 except KeyError:
181 if name in self.module_paths:
182 return None
183 else:
184 raise ImportError
185
186 def write_bytecode(self, name, bytecode):
187 self.module_bytecode[name] = bytecode
188 return True
189
190 def get_data(self, path):
191 if path in self.path_to_module:
192 return super().get_data(path)
193 elif path in self.path_to_bytecode:
194 name = self.path_to_bytecode[path]
195 return self.module_bytecode[name]
196 else:
197 raise IOError
198
199 def is_package(self, name):
200 try:
201 return super().is_package(name)
202 except TypeError:
203 return '__init__' in self.bytecode_to_path[name]
204
Brett Cannonf23e3742010-06-27 23:57:46 +0000205 def get_code(self, name):
206 with warnings.catch_warnings(record=True) as w:
207 warnings.simplefilter("always")
208 code_object = super().get_code(name)
209 assert len(w) == 1
Florent Xicluna67317752011-12-10 11:07:42 +0100210 assert issubclass(w[0].category, DeprecationWarning)
Brett Cannonf23e3742010-06-27 23:57:46 +0000211 return code_object
Brett Cannon2a922ed2009-03-09 03:35:50 +0000212
213class PyLoaderTests(testing_abc.LoaderTests):
214
215 """Tests for importlib.abc.PyLoader."""
216
217 mocker = PyLoaderMock
218
219 def eq_attrs(self, ob, **kwargs):
220 for attr, val in kwargs.items():
Brett Cannon978259e2009-04-02 15:32:07 +0000221 found = getattr(ob, attr)
222 self.assertEqual(found, val,
223 "{} attribute: {} != {}".format(attr, found, val))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000224
225 def test_module(self):
226 name = '<module>'
Brett Cannon55619822009-04-02 17:54:43 +0000227 path = os.path.join('', 'path', 'to', 'module')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000228 mock = self.mocker({name: path})
229 with util.uncache(name):
230 module = mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400231 self.assertIn(name, sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000232 self.eq_attrs(module, __name__=name, __file__=path, __package__='',
233 __loader__=mock)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000234 self.assertTrue(not hasattr(module, '__path__'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000235 return mock, name
236
237 def test_package(self):
238 name = '<pkg>'
Brett Cannon55619822009-04-02 17:54:43 +0000239 path = os.path.join('path', 'to', name, '__init__')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000240 mock = self.mocker({name: path})
241 with util.uncache(name):
242 module = mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400243 self.assertIn(name, sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000244 self.eq_attrs(module, __name__=name, __file__=path,
245 __path__=[os.path.dirname(path)], __package__=name,
246 __loader__=mock)
247 return mock, name
248
249 def test_lacking_parent(self):
250 name = 'pkg.mod'
Brett Cannon55619822009-04-02 17:54:43 +0000251 path = os.path.join('path', 'to', 'pkg', 'mod')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000252 mock = self.mocker({name: path})
253 with util.uncache(name):
254 module = mock.load_module(name)
Brett Cannon1262e7c2009-05-11 01:47:11 +0000255 self.assertIn(name, sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000256 self.eq_attrs(module, __name__=name, __file__=path, __package__='pkg',
257 __loader__=mock)
Brett Cannon1262e7c2009-05-11 01:47:11 +0000258 self.assertFalse(hasattr(module, '__path__'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000259 return mock, name
260
261 def test_module_reuse(self):
262 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000263 path = os.path.join('path', 'to', 'mod')
Brett Cannon2a922ed2009-03-09 03:35:50 +0000264 module = imp.new_module(name)
265 mock = self.mocker({name: path})
266 with util.uncache(name):
267 sys.modules[name] = module
268 loaded_module = mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400269 self.assertIs(loaded_module, module)
270 self.assertIs(sys.modules[name], module)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000271 return mock, name
272
273 def test_state_after_failure(self):
274 name = "mod"
275 module = imp.new_module(name)
276 module.blah = None
Brett Cannon55619822009-04-02 17:54:43 +0000277 mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
Brett Cannond43b30b2009-03-10 03:29:23 +0000278 mock.source = b"1/0"
Brett Cannon2a922ed2009-03-09 03:35:50 +0000279 with util.uncache(name):
280 sys.modules[name] = module
Brett Cannon0a49c582009-07-19 23:43:45 +0000281 with self.assertRaises(ZeroDivisionError):
282 mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400283 self.assertIs(sys.modules[name], module)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000284 self.assertTrue(hasattr(module, 'blah'))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000285 return mock
286
287 def test_unloadable(self):
288 name = "mod"
Brett Cannon55619822009-04-02 17:54:43 +0000289 mock = self.mocker({name: os.path.join('path', 'to', 'mod')})
Brett Cannond43b30b2009-03-10 03:29:23 +0000290 mock.source = b"1/0"
Brett Cannon2a922ed2009-03-09 03:35:50 +0000291 with util.uncache(name):
Brett Cannon0a49c582009-07-19 23:43:45 +0000292 with self.assertRaises(ZeroDivisionError):
293 mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400294 self.assertNotIn(name, sys.modules)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000295 return mock
296
297
Brett Cannonf23e3742010-06-27 23:57:46 +0000298class PyLoaderCompatTests(PyLoaderTests):
299
300 """Test that the suggested code to make a loader that is compatible from
301 Python 3.1 forward works."""
302
303 mocker = PyLoaderCompatMock
304
305
Brett Cannon2a922ed2009-03-09 03:35:50 +0000306class PyLoaderInterfaceTests(unittest.TestCase):
307
Brett Cannond43b30b2009-03-10 03:29:23 +0000308 """Tests for importlib.abc.PyLoader to make sure that when source_path()
309 doesn't return a path everything works as expected."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000310
311 def test_no_source_path(self):
312 # No source path should lead to ImportError.
313 name = 'mod'
314 mock = PyLoaderMock({})
Brett Cannon0a49c582009-07-19 23:43:45 +0000315 with util.uncache(name), self.assertRaises(ImportError):
316 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000317
318 def test_source_path_is_None(self):
319 name = 'mod'
320 mock = PyLoaderMock({name: None})
Brett Cannon0a49c582009-07-19 23:43:45 +0000321 with util.uncache(name), self.assertRaises(ImportError):
322 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000323
Brett Cannon69194272009-07-20 04:23:48 +0000324 def test_get_filename_with_source_path(self):
325 # get_filename() should return what source_path() returns.
326 name = 'mod'
327 path = os.path.join('path', 'to', 'source')
328 mock = PyLoaderMock({name: path})
329 with util.uncache(name):
330 self.assertEqual(mock.get_filename(name), path)
331
332 def test_get_filename_no_source_path(self):
333 # get_filename() should raise ImportError if source_path returns None.
334 name = 'mod'
335 mock = PyLoaderMock({name: None})
336 with util.uncache(name), self.assertRaises(ImportError):
337 mock.get_filename(name)
338
Brett Cannon2a922ed2009-03-09 03:35:50 +0000339
340class PyPycLoaderTests(PyLoaderTests):
341
342 """Tests for importlib.abc.PyPycLoader."""
343
344 mocker = PyPycLoaderMock
345
Brett Cannon1262e7c2009-05-11 01:47:11 +0000346 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000347 def verify_bytecode(self, mock, name):
348 assert name in mock.module_paths
Brett Cannon1262e7c2009-05-11 01:47:11 +0000349 self.assertIn(name, mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000350 magic = mock.module_bytecode[name][:4]
351 self.assertEqual(magic, imp.get_magic())
352 mtime = importlib._r_long(mock.module_bytecode[name][4:8])
353 self.assertEqual(mtime, 1)
Brett Cannon1e331562012-07-02 14:35:34 -0400354 source_size = mock.module_bytecode[name][8:12]
355 self.assertEqual(len(mock.source) & 0xFFFFFFFF,
356 importlib._r_long(source_size))
357 bc = mock.module_bytecode[name][12:]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000358 self.assertEqual(bc, mock.compile_bc(name))
Brett Cannon2a922ed2009-03-09 03:35:50 +0000359
360 def test_module(self):
361 mock, name = super().test_module()
362 self.verify_bytecode(mock, name)
363
364 def test_package(self):
365 mock, name = super().test_package()
366 self.verify_bytecode(mock, name)
367
368 def test_lacking_parent(self):
369 mock, name = super().test_lacking_parent()
370 self.verify_bytecode(mock, name)
371
372 def test_module_reuse(self):
373 mock, name = super().test_module_reuse()
374 self.verify_bytecode(mock, name)
375
376 def test_state_after_failure(self):
377 super().test_state_after_failure()
378
379 def test_unloadable(self):
380 super().test_unloadable()
381
382
Brett Cannon69194272009-07-20 04:23:48 +0000383class PyPycLoaderInterfaceTests(unittest.TestCase):
384
385 """Test for the interface of importlib.abc.PyPycLoader."""
386
387 def get_filename_check(self, src_path, bc_path, expect):
388 name = 'mod'
389 mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}})
390 with util.uncache(name):
391 assert mock.source_path(name) == src_path
392 assert mock.bytecode_path(name) == bc_path
393 self.assertEqual(mock.get_filename(name), expect)
394
395 def test_filename_with_source_bc(self):
396 # When source and bytecode paths present, return the source path.
397 self.get_filename_check('source_path', 'bc_path', 'source_path')
398
399 def test_filename_with_source_no_bc(self):
400 # With source but no bc, return source path.
401 self.get_filename_check('source_path', None, 'source_path')
402
403 def test_filename_with_no_source_bc(self):
404 # With not source but bc, return the bc path.
405 self.get_filename_check(None, 'bc_path', 'bc_path')
406
407 def test_filename_with_no_source_or_bc(self):
408 # With no source or bc, raise ImportError.
409 name = 'mod'
410 mock = PyPycLoaderMock({name: None}, {name: {'path': None}})
411 with util.uncache(name), self.assertRaises(ImportError):
412 mock.get_filename(name)
413
414
Brett Cannon2a922ed2009-03-09 03:35:50 +0000415class SkipWritingBytecodeTests(unittest.TestCase):
416
417 """Test that bytecode is properly handled based on
418 sys.dont_write_bytecode."""
419
Brett Cannon1262e7c2009-05-11 01:47:11 +0000420 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000421 def run_test(self, dont_write_bytecode):
422 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000423 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
Brett Cannon2a922ed2009-03-09 03:35:50 +0000424 sys.dont_write_bytecode = dont_write_bytecode
425 with util.uncache(name):
426 mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400427 self.assertIsNot(name in mock.module_bytecode, dont_write_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000428
429 def test_no_bytecode_written(self):
430 self.run_test(True)
431
432 def test_bytecode_written(self):
433 self.run_test(False)
434
435
436class RegeneratedBytecodeTests(unittest.TestCase):
437
438 """Test that bytecode is regenerated as expected."""
439
Brett Cannon1262e7c2009-05-11 01:47:11 +0000440 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000441 def test_different_magic(self):
442 # A different magic number should lead to new bytecode.
443 name = 'mod'
444 bad_magic = b'\x00\x00\x00\x00'
445 assert bad_magic != imp.get_magic()
Brett Cannon55619822009-04-02 17:54:43 +0000446 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
447 {name: {'path': os.path.join('path', 'to',
448 'mod.bytecode'),
Brett Cannon2a922ed2009-03-09 03:35:50 +0000449 'magic': bad_magic}})
450 with util.uncache(name):
451 mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400452 self.assertIn(name, mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000453 magic = mock.module_bytecode[name][:4]
454 self.assertEqual(magic, imp.get_magic())
455
Brett Cannon1262e7c2009-05-11 01:47:11 +0000456 @source_util.writes_bytecode_files
Brett Cannon2a922ed2009-03-09 03:35:50 +0000457 def test_old_mtime(self):
458 # Bytecode with an older mtime should be regenerated.
459 name = 'mod'
460 old_mtime = PyPycLoaderMock.default_mtime - 1
Brett Cannon55619822009-04-02 17:54:43 +0000461 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')},
Brett Cannon2a922ed2009-03-09 03:35:50 +0000462 {name: {'path': 'path/to/mod.bytecode', 'mtime': old_mtime}})
463 with util.uncache(name):
464 mock.load_module(name)
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400465 self.assertIn(name, mock.module_bytecode)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000466 mtime = importlib._r_long(mock.module_bytecode[name][4:8])
467 self.assertEqual(mtime, PyPycLoaderMock.default_mtime)
468
469
470class BadBytecodeFailureTests(unittest.TestCase):
471
472 """Test import failures when there is no source and parts of the bytecode
473 is bad."""
474
475 def test_bad_magic(self):
476 # A bad magic number should lead to an ImportError.
477 name = 'mod'
478 bad_magic = b'\x00\x00\x00\x00'
Brett Cannon3c273842009-07-20 00:14:29 +0000479 bc = {name:
480 {'path': os.path.join('path', 'to', 'mod'),
481 'magic': bad_magic}}
482 mock = PyPycLoaderMock({name: None}, bc)
Brett Cannonbbb66802012-04-12 21:09:01 -0400483 with util.uncache(name), self.assertRaises(ImportError) as cm:
Brett Cannon0a49c582009-07-19 23:43:45 +0000484 mock.load_module(name)
Brett Cannonbbb66802012-04-12 21:09:01 -0400485 self.assertEqual(cm.exception.name, name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000486
Brett Cannon3c273842009-07-20 00:14:29 +0000487 def test_no_bytecode(self):
488 # Missing code object bytecode should lead to an EOFError.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000489 name = 'mod'
Brett Cannon3c273842009-07-20 00:14:29 +0000490 bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b''}}
491 mock = PyPycLoaderMock({name: None}, bc)
492 with util.uncache(name), self.assertRaises(EOFError):
493 mock.load_module(name)
494
495 def test_bad_bytecode(self):
496 # Malformed code object bytecode should lead to a ValueError.
497 name = 'mod'
Benjamin Peterson2215c142010-06-28 00:24:13 +0000498 bc = {name: {'path': os.path.join('path', 'to', 'mod'), 'bc': b'1234'}}
Brett Cannon3c273842009-07-20 00:14:29 +0000499 mock = PyPycLoaderMock({name: None}, bc)
500 with util.uncache(name), self.assertRaises(ValueError):
Brett Cannon0a49c582009-07-19 23:43:45 +0000501 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000502
503
504def raise_ImportError(*args, **kwargs):
505 raise ImportError
506
507class MissingPathsTests(unittest.TestCase):
508
509 """Test what happens when a source or bytecode path does not exist (either
510 from *_path returning None or raising ImportError)."""
511
512 def test_source_path_None(self):
513 # Bytecode should be used when source_path returns None, along with
514 # __file__ being set to the bytecode path.
515 name = 'mod'
516 bytecode_path = 'path/to/mod'
517 mock = PyPycLoaderMock({name: None}, {name: {'path': bytecode_path}})
518 with util.uncache(name):
519 module = mock.load_module(name)
520 self.assertEqual(module.__file__, bytecode_path)
521
522 # Testing for bytecode_path returning None handled by all tests where no
523 # bytecode initially exists.
524
525 def test_all_paths_None(self):
526 # If all *_path methods return None, raise ImportError.
527 name = 'mod'
528 mock = PyPycLoaderMock({name: None})
Brett Cannonbbb66802012-04-12 21:09:01 -0400529 with util.uncache(name), self.assertRaises(ImportError) as cm:
Brett Cannon0a49c582009-07-19 23:43:45 +0000530 mock.load_module(name)
Brett Cannonbbb66802012-04-12 21:09:01 -0400531 self.assertEqual(cm.exception.name, name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000532
533 def test_source_path_ImportError(self):
534 # An ImportError from source_path should trigger an ImportError.
535 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000536 mock = PyPycLoaderMock({}, {name: {'path': os.path.join('path', 'to',
537 'mod')}})
Brett Cannon0a49c582009-07-19 23:43:45 +0000538 with util.uncache(name), self.assertRaises(ImportError):
539 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000540
541 def test_bytecode_path_ImportError(self):
542 # An ImportError from bytecode_path should trigger an ImportError.
543 name = 'mod'
Brett Cannon55619822009-04-02 17:54:43 +0000544 mock = PyPycLoaderMock({name: os.path.join('path', 'to', 'mod')})
Brett Cannon2a922ed2009-03-09 03:35:50 +0000545 bad_meth = types.MethodType(raise_ImportError, mock)
546 mock.bytecode_path = bad_meth
Brett Cannonbbb66802012-04-12 21:09:01 -0400547 with util.uncache(name), self.assertRaises(ImportError) as cm:
Brett Cannon0a49c582009-07-19 23:43:45 +0000548 mock.load_module(name)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000549
550
Brett Cannonf23e3742010-06-27 23:57:46 +0000551class SourceLoaderTestHarness(unittest.TestCase):
552
553 def setUp(self, *, is_package=True, **kwargs):
554 self.package = 'pkg'
555 if is_package:
556 self.path = os.path.join(self.package, '__init__.py')
557 self.name = self.package
558 else:
559 module_name = 'mod'
560 self.path = os.path.join(self.package, '.'.join(['mod', 'py']))
561 self.name = '.'.join([self.package, module_name])
562 self.cached = imp.cache_from_source(self.path)
563 self.loader = self.loader_mock(self.path, **kwargs)
564
565 def verify_module(self, module):
566 self.assertEqual(module.__name__, self.name)
567 self.assertEqual(module.__file__, self.path)
568 self.assertEqual(module.__cached__, self.cached)
569 self.assertEqual(module.__package__, self.package)
570 self.assertEqual(module.__loader__, self.loader)
571 values = module._.split('::')
572 self.assertEqual(values[0], self.name)
573 self.assertEqual(values[1], self.path)
574 self.assertEqual(values[2], self.cached)
575 self.assertEqual(values[3], self.package)
576 self.assertEqual(values[4], repr(self.loader))
577
578 def verify_code(self, code_object):
579 module = imp.new_module(self.name)
580 module.__file__ = self.path
581 module.__cached__ = self.cached
582 module.__package__ = self.package
583 module.__loader__ = self.loader
584 module.__path__ = []
585 exec(code_object, module.__dict__)
586 self.verify_module(module)
587
588
589class SourceOnlyLoaderTests(SourceLoaderTestHarness):
590
591 """Test importlib.abc.SourceLoader for source-only loading.
592
593 Reload testing is subsumed by the tests for
594 importlib.util.module_for_loader.
595
596 """
597
598 loader_mock = SourceOnlyLoaderMock
599
600 def test_get_source(self):
601 # Verify the source code is returned as a string.
602 # If an IOError is raised by get_data then raise ImportError.
603 expected_source = self.loader.source.decode('utf-8')
604 self.assertEqual(self.loader.get_source(self.name), expected_source)
605 def raise_IOError(path):
606 raise IOError
607 self.loader.get_data = raise_IOError
Brett Cannonbbb66802012-04-12 21:09:01 -0400608 with self.assertRaises(ImportError) as cm:
Brett Cannonf23e3742010-06-27 23:57:46 +0000609 self.loader.get_source(self.name)
Brett Cannonbbb66802012-04-12 21:09:01 -0400610 self.assertEqual(cm.exception.name, self.name)
Brett Cannonf23e3742010-06-27 23:57:46 +0000611
612 def test_is_package(self):
613 # Properly detect when loading a package.
Brett Cannonf23e3742010-06-27 23:57:46 +0000614 self.setUp(is_package=False)
615 self.assertFalse(self.loader.is_package(self.name))
Brett Cannonea0b8232012-06-15 20:00:53 -0400616 self.setUp(is_package=True)
617 self.assertTrue(self.loader.is_package(self.name))
618 self.assertFalse(self.loader.is_package(self.name + '.__init__'))
Brett Cannonf23e3742010-06-27 23:57:46 +0000619
620 def test_get_code(self):
621 # Verify the code object is created.
622 code_object = self.loader.get_code(self.name)
623 self.verify_code(code_object)
624
625 def test_load_module(self):
626 # Loading a module should set __name__, __loader__, __package__,
627 # __path__ (for packages), __file__, and __cached__.
628 # The module should also be put into sys.modules.
629 with util.uncache(self.name):
630 module = self.loader.load_module(self.name)
631 self.verify_module(module)
632 self.assertEqual(module.__path__, [os.path.dirname(self.path)])
Eric V. Smithfaae3ad2012-06-27 15:26:26 -0400633 self.assertIn(self.name, sys.modules)
Brett Cannonf23e3742010-06-27 23:57:46 +0000634
635 def test_package_settings(self):
636 # __package__ needs to be set, while __path__ is set on if the module
637 # is a package.
638 # Testing the values for a package are covered by test_load_module.
639 self.setUp(is_package=False)
640 with util.uncache(self.name):
641 module = self.loader.load_module(self.name)
642 self.verify_module(module)
643 self.assertTrue(not hasattr(module, '__path__'))
644
645 def test_get_source_encoding(self):
646 # Source is considered encoded in UTF-8 by default unless otherwise
647 # specified by an encoding line.
648 source = "_ = 'ü'"
649 self.loader.source = source.encode('utf-8')
650 returned_source = self.loader.get_source(self.name)
651 self.assertEqual(returned_source, source)
652 source = "# coding: latin-1\n_ = ü"
653 self.loader.source = source.encode('latin-1')
654 returned_source = self.loader.get_source(self.name)
655 self.assertEqual(returned_source, source)
656
657
658@unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true")
659class SourceLoaderBytecodeTests(SourceLoaderTestHarness):
660
661 """Test importlib.abc.SourceLoader's use of bytecode.
662
663 Source-only testing handled by SourceOnlyLoaderTests.
664
665 """
666
667 loader_mock = SourceLoaderMock
668
669 def verify_code(self, code_object, *, bytecode_written=False):
670 super().verify_code(code_object)
671 if bytecode_written:
672 self.assertIn(self.cached, self.loader.written)
673 data = bytearray(imp.get_magic())
Brett Cannonc264e3e2012-01-25 18:58:03 -0500674 data.extend(importlib._w_long(self.loader.source_mtime))
675 data.extend(importlib._w_long(self.loader.source_size))
Brett Cannonf23e3742010-06-27 23:57:46 +0000676 data.extend(marshal.dumps(code_object))
677 self.assertEqual(self.loader.written[self.cached], bytes(data))
678
679 def test_code_with_everything(self):
680 # When everything should work.
681 code_object = self.loader.get_code(self.name)
682 self.verify_code(code_object)
683
684 def test_no_bytecode(self):
685 # If no bytecode exists then move on to the source.
686 self.loader.bytecode_path = "<does not exist>"
687 # Sanity check
688 with self.assertRaises(IOError):
689 bytecode_path = imp.cache_from_source(self.path)
690 self.loader.get_data(bytecode_path)
691 code_object = self.loader.get_code(self.name)
692 self.verify_code(code_object, bytecode_written=True)
693
694 def test_code_bad_timestamp(self):
695 # Bytecode is only used when the timestamp matches the source EXACTLY.
696 for source_mtime in (0, 2):
697 assert source_mtime != self.loader.source_mtime
698 original = self.loader.source_mtime
699 self.loader.source_mtime = source_mtime
700 # If bytecode is used then EOFError would be raised by marshal.
701 self.loader.bytecode = self.loader.bytecode[8:]
702 code_object = self.loader.get_code(self.name)
703 self.verify_code(code_object, bytecode_written=True)
704 self.loader.source_mtime = original
705
706 def test_code_bad_magic(self):
707 # Skip over bytecode with a bad magic number.
708 self.setUp(magic=b'0000')
709 # If bytecode is used then EOFError would be raised by marshal.
710 self.loader.bytecode = self.loader.bytecode[8:]
711 code_object = self.loader.get_code(self.name)
712 self.verify_code(code_object, bytecode_written=True)
713
714 def test_dont_write_bytecode(self):
715 # Bytecode is not written if sys.dont_write_bytecode is true.
716 # Can assume it is false already thanks to the skipIf class decorator.
717 try:
718 sys.dont_write_bytecode = True
719 self.loader.bytecode_path = "<does not exist>"
720 code_object = self.loader.get_code(self.name)
721 self.assertNotIn(self.cached, self.loader.written)
722 finally:
723 sys.dont_write_bytecode = False
724
725 def test_no_set_data(self):
726 # If set_data is not defined, one can still read bytecode.
727 self.setUp(magic=b'0000')
728 original_set_data = self.loader.__class__.set_data
729 try:
730 del self.loader.__class__.set_data
731 code_object = self.loader.get_code(self.name)
732 self.verify_code(code_object)
733 finally:
734 self.loader.__class__.set_data = original_set_data
735
736 def test_set_data_raises_exceptions(self):
737 # Raising NotImplementedError or IOError is okay for set_data.
738 def raise_exception(exc):
739 def closure(*args, **kwargs):
740 raise exc
741 return closure
742
743 self.setUp(magic=b'0000')
Brett Cannon0cf9e6a2010-06-28 04:57:24 +0000744 self.loader.set_data = raise_exception(NotImplementedError)
745 code_object = self.loader.get_code(self.name)
746 self.verify_code(code_object)
Brett Cannonf23e3742010-06-27 23:57:46 +0000747
Brett Cannon418182e2010-07-03 22:32:41 +0000748
749class SourceLoaderGetSourceTests(unittest.TestCase):
750
751 """Tests for importlib.abc.SourceLoader.get_source()."""
752
753 def test_default_encoding(self):
754 # Should have no problems with UTF-8 text.
755 name = 'mod'
756 mock = SourceOnlyLoaderMock('mod.file')
757 source = 'x = "ü"'
758 mock.source = source.encode('utf-8')
759 returned_source = mock.get_source(name)
760 self.assertEqual(returned_source, source)
761
762 def test_decoded_source(self):
763 # Decoding should work.
764 name = 'mod'
765 mock = SourceOnlyLoaderMock("mod.file")
766 source = "# coding: Latin-1\nx='ü'"
767 assert source.encode('latin-1') != source.encode('utf-8')
768 mock.source = source.encode('latin-1')
769 returned_source = mock.get_source(name)
770 self.assertEqual(returned_source, source)
771
772 def test_universal_newlines(self):
773 # PEP 302 says universal newlines should be used.
774 name = 'mod'
775 mock = SourceOnlyLoaderMock('mod.file')
776 source = "x = 42\r\ny = -13\r\n"
777 mock.source = source.encode('utf-8')
778 expect = io.IncrementalNewlineDecoder(None, True).decode(source)
779 self.assertEqual(mock.get_source(name), expect)
780
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000781
Brett Cannonf23e3742010-06-27 23:57:46 +0000782class AbstractMethodImplTests(unittest.TestCase):
783
784 """Test the concrete abstractmethod implementations."""
785
Nick Coghlan8a9080f2012-08-02 21:26:03 +1000786 class MetaPathFinder(abc.MetaPathFinder):
787 def find_module(self, fullname, path):
788 super().find_module(fullname, path)
789
790 class PathEntryFinder(abc.PathEntryFinder):
791 def find_module(self, _):
792 super().find_module(_)
793
794 def find_loader(self, _):
795 super().find_loader(_)
796
797 class Finder(abc.Finder):
798 def find_module(self, fullname, path):
799 super().find_module(fullname, path)
800
Brett Cannonf23e3742010-06-27 23:57:46 +0000801 class Loader(abc.Loader):
802 def load_module(self, fullname):
803 super().load_module(fullname)
Barry Warsaw0efcf992012-07-31 17:52:32 -0400804 def module_repr(self, module):
805 super().module_repr(module)
Brett Cannonf23e3742010-06-27 23:57:46 +0000806
Brett Cannonf23e3742010-06-27 23:57:46 +0000807 class ResourceLoader(Loader, abc.ResourceLoader):
808 def get_data(self, _):
809 super().get_data(_)
810
811 class InspectLoader(Loader, abc.InspectLoader):
812 def is_package(self, _):
813 super().is_package(_)
814
815 def get_code(self, _):
816 super().get_code(_)
817
818 def get_source(self, _):
819 super().get_source(_)
820
821 class ExecutionLoader(InspectLoader, abc.ExecutionLoader):
822 def get_filename(self, _):
823 super().get_filename(_)
824
825 class SourceLoader(ResourceLoader, ExecutionLoader, abc.SourceLoader):
826 pass
827
828 class PyLoader(ResourceLoader, InspectLoader, abc.PyLoader):
829 def source_path(self, _):
830 super().source_path(_)
831
832 class PyPycLoader(PyLoader, abc.PyPycLoader):
833 def bytecode_path(self, _):
834 super().bytecode_path(_)
835
836 def source_mtime(self, _):
837 super().source_mtime(_)
838
839 def write_bytecode(self, _, _2):
840 super().write_bytecode(_, _2)
841
842 def raises_NotImplementedError(self, ins, *args):
843 for method_name in args:
844 method = getattr(ins, method_name)
845 arg_count = len(inspect.getfullargspec(method)[0]) - 1
846 args = [''] * arg_count
847 try:
848 method(*args)
849 except NotImplementedError:
850 pass
851 else:
852 msg = "{}.{} did not raise NotImplementedError"
853 self.fail(msg.format(ins.__class__.__name__, method_name))
854
855 def test_Loader(self):
856 self.raises_NotImplementedError(self.Loader(), 'load_module')
857
Brett Cannon61b14252010-07-03 21:48:25 +0000858 # XXX misplaced; should be somewhere else
Brett Cannonf23e3742010-06-27 23:57:46 +0000859 def test_Finder(self):
860 self.raises_NotImplementedError(self.Finder(), 'find_module')
861
862 def test_ResourceLoader(self):
863 self.raises_NotImplementedError(self.ResourceLoader(), 'load_module',
864 'get_data')
865
866 def test_InspectLoader(self):
867 self.raises_NotImplementedError(self.InspectLoader(), 'load_module',
868 'is_package', 'get_code', 'get_source')
869
870 def test_ExecutionLoader(self):
871 self.raises_NotImplementedError(self.ExecutionLoader(), 'load_module',
872 'is_package', 'get_code', 'get_source',
873 'get_filename')
874
875 def test_SourceLoader(self):
876 ins = self.SourceLoader()
877 # Required abstractmethods.
878 self.raises_NotImplementedError(ins, 'get_filename', 'get_data')
879 # Optional abstractmethods.
Antoine Pitrou5136ac02012-01-13 18:52:16 +0100880 self.raises_NotImplementedError(ins,'path_stats', 'set_data')
Brett Cannonf23e3742010-06-27 23:57:46 +0000881
882 def test_PyLoader(self):
883 self.raises_NotImplementedError(self.PyLoader(), 'source_path',
884 'get_data', 'is_package')
885
886 def test_PyPycLoader(self):
887 self.raises_NotImplementedError(self.PyPycLoader(), 'source_path',
888 'source_mtime', 'bytecode_path',
889 'write_bytecode')
890
891
Brett Cannon2a922ed2009-03-09 03:35:50 +0000892def test_main():
893 from test.support import run_unittest
Brett Cannonf23e3742010-06-27 23:57:46 +0000894 run_unittest(PyLoaderTests, PyLoaderCompatTests,
Brett Cannon418182e2010-07-03 22:32:41 +0000895 PyLoaderInterfaceTests,
Brett Cannon69194272009-07-20 04:23:48 +0000896 PyPycLoaderTests, PyPycLoaderInterfaceTests,
897 SkipWritingBytecodeTests, RegeneratedBytecodeTests,
Brett Cannonf23e3742010-06-27 23:57:46 +0000898 BadBytecodeFailureTests, MissingPathsTests,
899 SourceOnlyLoaderTests,
900 SourceLoaderBytecodeTests,
Brett Cannon418182e2010-07-03 22:32:41 +0000901 SourceLoaderGetSourceTests,
Brett Cannonf23e3742010-06-27 23:57:46 +0000902 AbstractMethodImplTests)
Brett Cannon2a922ed2009-03-09 03:35:50 +0000903
904
905if __name__ == '__main__':
906 test_main()