blob: 89990be9da92f4267e68eb63134eaadc8cc2c935 [file] [log] [blame]
Brett Cannon23cbd8a2009-01-18 00:24:28 +00001import importlib
Brett Cannonf87e04d2009-03-12 22:47:53 +00002from importlib import _bootstrap
Brett Cannon30b047d2009-02-01 02:05:11 +00003from .. import abc
Brett Cannond71bed32010-07-03 22:18:47 +00004from .. import util
Brett Cannon4ee2cda2009-02-01 03:08:31 +00005from . import util as source_util
Brett Cannon23cbd8a2009-01-18 00:24:28 +00006
7import imp
Brett Cannon61b14252010-07-03 21:48:25 +00008import marshal
Brett Cannon23cbd8a2009-01-18 00:24:28 +00009import os
10import py_compile
Brett Cannon186335b2010-08-22 22:11:06 +000011import shutil
Brett Cannone52c9192009-11-07 23:55:05 +000012import stat
Brett Cannon23cbd8a2009-01-18 00:24:28 +000013import sys
14import unittest
15
Barry Warsaw04b56842010-05-18 14:15:20 +000016from test.support import make_legacy_pyc
17
Brett Cannon23cbd8a2009-01-18 00:24:28 +000018
19class SimpleTest(unittest.TestCase):
20
21 """Should have no issue importing a source module [basic]. And if there is
22 a syntax error, it should raise a SyntaxError [syntax error].
23
24 """
25
26 # [basic]
Brett Cannon30b047d2009-02-01 02:05:11 +000027 def test_module(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000028 with source_util.create_modules('_temp') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000029 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon30b047d2009-02-01 02:05:11 +000030 module = loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000031 self.assertTrue('_temp' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000032 check = {'__name__': '_temp', '__file__': mapping['_temp'],
Brett Cannon06c9d962009-02-07 01:52:25 +000033 '__package__': ''}
Brett Cannon30b047d2009-02-01 02:05:11 +000034 for attr, value in check.items():
35 self.assertEqual(getattr(module, attr), value)
36
37 def test_package(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000038 with source_util.create_modules('_pkg.__init__') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000039 loader = _bootstrap._SourceFileLoader('_pkg',
40 mapping['_pkg.__init__'])
Brett Cannon30b047d2009-02-01 02:05:11 +000041 module = loader.load_module('_pkg')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000042 self.assertTrue('_pkg' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000043 check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'],
44 '__path__': [os.path.dirname(mapping['_pkg.__init__'])],
45 '__package__': '_pkg'}
46 for attr, value in check.items():
47 self.assertEqual(getattr(module, attr), value)
48
49
50 def test_lacking_parent(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000051 with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000052 loader = _bootstrap._SourceFileLoader('_pkg.mod',
53 mapping['_pkg.mod'])
Brett Cannon30b047d2009-02-01 02:05:11 +000054 module = loader.load_module('_pkg.mod')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000055 self.assertTrue('_pkg.mod' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000056 check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'],
57 '__package__': '_pkg'}
58 for attr, value in check.items():
59 self.assertEqual(getattr(module, attr), value)
60
61 def fake_mtime(self, fxn):
62 """Fake mtime to always be higher than expected."""
63 return lambda name: fxn(name) + 1
64
65 def test_module_reuse(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000066 with source_util.create_modules('_temp') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000067 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon30b047d2009-02-01 02:05:11 +000068 module = loader.load_module('_temp')
69 module_id = id(module)
70 module_dict_id = id(module.__dict__)
71 with open(mapping['_temp'], 'w') as file:
72 file.write("testing_var = 42\n")
73 # For filesystems where the mtime is only to a second granularity,
74 # everything that has happened above can be too fast;
75 # force an mtime on the source that is guaranteed to be different
76 # than the original mtime.
Brett Cannon61b14252010-07-03 21:48:25 +000077 loader.path_mtime = self.fake_mtime(loader.path_mtime)
Brett Cannon30b047d2009-02-01 02:05:11 +000078 module = loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000079 self.assertTrue('testing_var' in module.__dict__,
Brett Cannon30b047d2009-02-01 02:05:11 +000080 "'testing_var' not in "
81 "{0}".format(list(module.__dict__.keys())))
82 self.assertEqual(module, sys.modules['_temp'])
83 self.assertEqual(id(module), module_id)
84 self.assertEqual(id(module.__dict__), module_dict_id)
85
86 def test_state_after_failure(self):
87 # A failed reload should leave the original module intact.
88 attributes = ('__file__', '__path__', '__package__')
89 value = '<test>'
90 name = '_temp'
Brett Cannon4ee2cda2009-02-01 03:08:31 +000091 with source_util.create_modules(name) as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000092 orig_module = imp.new_module(name)
93 for attr in attributes:
94 setattr(orig_module, attr, value)
95 with open(mapping[name], 'w') as file:
96 file.write('+++ bad syntax +++')
Brett Cannon61b14252010-07-03 21:48:25 +000097 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon2153dc02009-08-27 23:49:21 +000098 with self.assertRaises(SyntaxError):
99 loader.load_module(name)
Brett Cannon30b047d2009-02-01 02:05:11 +0000100 for attr in attributes:
101 self.assertEqual(getattr(orig_module, attr), value)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000102
103 # [syntax error]
104 def test_bad_syntax(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000105 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000106 with open(mapping['_temp'], 'w') as file:
107 file.write('=')
Brett Cannon61b14252010-07-03 21:48:25 +0000108 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon2153dc02009-08-27 23:49:21 +0000109 with self.assertRaises(SyntaxError):
110 loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000111 self.assertTrue('_temp' not in sys.modules)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000112
Brett Cannond71bed32010-07-03 22:18:47 +0000113 def test_file_from_empty_string_dir(self):
114 # Loading a module found from an empty string entry on sys.path should
115 # not only work, but keep all attributes relative.
Brett Cannon186335b2010-08-22 22:11:06 +0000116 file_path = '_temp.py'
117 with open(file_path, 'w') as file:
Brett Cannond71bed32010-07-03 22:18:47 +0000118 file.write("# test file for importlib")
119 try:
120 with util.uncache('_temp'):
Brett Cannon186335b2010-08-22 22:11:06 +0000121 loader = _bootstrap._SourceFileLoader('_temp', file_path)
Brett Cannond71bed32010-07-03 22:18:47 +0000122 mod = loader.load_module('_temp')
Brett Cannon186335b2010-08-22 22:11:06 +0000123 self.assertEqual(file_path, mod.__file__)
124 self.assertEqual(imp.cache_from_source(file_path),
Brett Cannond71bed32010-07-03 22:18:47 +0000125 mod.__cached__)
Brett Cannond71bed32010-07-03 22:18:47 +0000126 finally:
Brett Cannon186335b2010-08-22 22:11:06 +0000127 os.unlink(file_path)
128 pycache = os.path.dirname(imp.cache_from_source(file_path))
129 shutil.rmtree(pycache)
Brett Cannond71bed32010-07-03 22:18:47 +0000130
Antoine Pitrou2be60af2012-01-24 17:44:06 +0100131 def test_timestamp_overflow(self):
132 # When a modification timestamp is larger than 2**32, it should be
133 # truncated rather than raise an OverflowError.
134 with source_util.create_modules('_temp') as mapping:
135 source = mapping['_temp']
136 compiled = imp.cache_from_source(source)
137 with open(source, 'w') as f:
138 f.write("x = 5")
139 os.utime(source, (2 ** 33, 2 ** 33))
140 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
141 mod = loader.load_module('_temp')
142 # Sanity checks.
143 self.assertEqual(mod.__cached__, compiled)
144 self.assertEqual(mod.x, 5)
145 # The pyc file was created.
146 os.stat(compiled)
147
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000148
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000149class BadBytecodeTest(unittest.TestCase):
150
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000151 def import_(self, file, module_name):
Brett Cannon61b14252010-07-03 21:48:25 +0000152 loader = self.loader(module_name, file)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000153 module = loader.load_module(module_name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000154 self.assertTrue(module_name in sys.modules)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000155
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000156 def manipulate_bytecode(self, name, mapping, manipulator, *,
157 del_source=False):
158 """Manipulate the bytecode of a module by passing it into a callable
159 that returns what to use as the new bytecode."""
160 try:
161 del sys.modules['_temp']
162 except KeyError:
163 pass
164 py_compile.compile(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000165 if not del_source:
166 bytecode_path = imp.cache_from_source(mapping[name])
167 else:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000168 os.unlink(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000169 bytecode_path = make_legacy_pyc(mapping[name])
170 if manipulator:
171 with open(bytecode_path, 'rb') as file:
172 bc = file.read()
173 new_bc = manipulator(bc)
174 with open(bytecode_path, 'wb') as file:
175 if new_bc is not None:
176 file.write(new_bc)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000177 return bytecode_path
178
Brett Cannon61b14252010-07-03 21:48:25 +0000179 def _test_empty_file(self, test, *, del_source=False):
180 with source_util.create_modules('_temp') as mapping:
181 bc_path = self.manipulate_bytecode('_temp', mapping,
182 lambda bc: b'',
183 del_source=del_source)
184 test('_temp', mapping, bc_path)
185
186 @source_util.writes_bytecode_files
187 def _test_partial_magic(self, test, *, del_source=False):
188 # When their are less than 4 bytes to a .pyc, regenerate it if
189 # possible, else raise ImportError.
190 with source_util.create_modules('_temp') as mapping:
191 bc_path = self.manipulate_bytecode('_temp', mapping,
192 lambda bc: bc[:3],
193 del_source=del_source)
194 test('_temp', mapping, bc_path)
195
196 def _test_magic_only(self, test, *, del_source=False):
197 with source_util.create_modules('_temp') as mapping:
198 bc_path = self.manipulate_bytecode('_temp', mapping,
199 lambda bc: bc[:4],
200 del_source=del_source)
201 test('_temp', mapping, bc_path)
202
203 def _test_partial_timestamp(self, test, *, del_source=False):
204 with source_util.create_modules('_temp') as mapping:
205 bc_path = self.manipulate_bytecode('_temp', mapping,
206 lambda bc: bc[:7],
207 del_source=del_source)
208 test('_temp', mapping, bc_path)
209
210 def _test_no_marshal(self, *, del_source=False):
211 with source_util.create_modules('_temp') as mapping:
212 bc_path = self.manipulate_bytecode('_temp', mapping,
213 lambda bc: bc[:8],
214 del_source=del_source)
215 file_path = mapping['_temp'] if not del_source else bc_path
216 with self.assertRaises(EOFError):
217 self.import_(file_path, '_temp')
218
219 def _test_non_code_marshal(self, *, del_source=False):
220 with source_util.create_modules('_temp') as mapping:
221 bytecode_path = self.manipulate_bytecode('_temp', mapping,
222 lambda bc: bc[:8] + marshal.dumps(b'abcd'),
223 del_source=del_source)
224 file_path = mapping['_temp'] if not del_source else bytecode_path
225 with self.assertRaises(ImportError):
226 self.import_(file_path, '_temp')
227
228 def _test_bad_marshal(self, *, del_source=False):
229 with source_util.create_modules('_temp') as mapping:
230 bytecode_path = self.manipulate_bytecode('_temp', mapping,
231 lambda bc: bc[:8] + b'<test>',
232 del_source=del_source)
233 file_path = mapping['_temp'] if not del_source else bytecode_path
Vinay Sajip5bdae3b2011-07-02 16:42:47 +0100234 with self.assertRaises(EOFError):
Brett Cannon61b14252010-07-03 21:48:25 +0000235 self.import_(file_path, '_temp')
236
237 def _test_bad_magic(self, test, *, del_source=False):
238 with source_util.create_modules('_temp') as mapping:
239 bc_path = self.manipulate_bytecode('_temp', mapping,
240 lambda bc: b'\x00\x00\x00\x00' + bc[4:])
241 test('_temp', mapping, bc_path)
242
243
244class SourceLoaderBadBytecodeTest(BadBytecodeTest):
245
246 loader = _bootstrap._SourceFileLoader
247
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000248 @source_util.writes_bytecode_files
249 def test_empty_file(self):
250 # When a .pyc is empty, regenerate it if possible, else raise
251 # ImportError.
Brett Cannon61b14252010-07-03 21:48:25 +0000252 def test(name, mapping, bytecode_path):
253 self.import_(mapping[name], name)
254 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000255 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000256
Brett Cannon61b14252010-07-03 21:48:25 +0000257 self._test_empty_file(test)
258
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000259 def test_partial_magic(self):
Brett Cannon61b14252010-07-03 21:48:25 +0000260 def test(name, mapping, bytecode_path):
261 self.import_(mapping[name], name)
262 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000263 self.assertGreater(len(file.read()), 8)
Brett Cannon61b14252010-07-03 21:48:25 +0000264
265 self._test_partial_magic(test)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000266
267 @source_util.writes_bytecode_files
268 def test_magic_only(self):
269 # When there is only the magic number, regenerate the .pyc if possible,
270 # else raise EOFError.
Brett Cannon61b14252010-07-03 21:48:25 +0000271 def test(name, mapping, bytecode_path):
272 self.import_(mapping[name], name)
273 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000274 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000275
Antoine Pitrou7c9907e2011-12-30 21:25:15 +0100276 self._test_magic_only(test)
277
Brett Cannon1262e7c2009-05-11 01:47:11 +0000278 @source_util.writes_bytecode_files
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000279 def test_bad_magic(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000280 # When the magic number is different, the bytecode should be
281 # regenerated.
Brett Cannon61b14252010-07-03 21:48:25 +0000282 def test(name, mapping, bytecode_path):
283 self.import_(mapping[name], name)
284 with open(bytecode_path, 'rb') as bytecode_file:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000285 self.assertEqual(bytecode_file.read(4), imp.get_magic())
286
Brett Cannon61b14252010-07-03 21:48:25 +0000287 self._test_bad_magic(test)
288
289 @source_util.writes_bytecode_files
290 def test_partial_timestamp(self):
291 # When the timestamp is partial, regenerate the .pyc, else
292 # raise EOFError.
293 def test(name, mapping, bc_path):
294 self.import_(mapping[name], name)
295 with open(bc_path, 'rb') as file:
296 self.assertGreater(len(file.read()), 8)
297
Antoine Pitrou7c9907e2011-12-30 21:25:15 +0100298 self._test_partial_timestamp(test)
299
Brett Cannon61b14252010-07-03 21:48:25 +0000300 @source_util.writes_bytecode_files
301 def test_no_marshal(self):
302 # When there is only the magic number and timestamp, raise EOFError.
303 self._test_no_marshal()
304
305 @source_util.writes_bytecode_files
306 def test_non_code_marshal(self):
307 self._test_non_code_marshal()
308 # XXX ImportError when sourceless
309
310 # [bad marshal]
311 @source_util.writes_bytecode_files
312 def test_bad_marshal(self):
313 # Bad marshal data should raise a ValueError.
314 self._test_bad_marshal()
315
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000316 # [bad timestamp]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000317 @source_util.writes_bytecode_files
Brett Cannon61b14252010-07-03 21:48:25 +0000318 def test_old_timestamp(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000319 # When the timestamp is older than the source, bytecode should be
320 # regenerated.
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000321 zeros = b'\x00\x00\x00\x00'
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000322 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000323 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000324 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000325 with open(bytecode_path, 'r+b') as bytecode_file:
326 bytecode_file.seek(4)
327 bytecode_file.write(zeros)
328 self.import_(mapping['_temp'], '_temp')
329 source_mtime = os.path.getmtime(mapping['_temp'])
330 source_timestamp = importlib._w_long(source_mtime)
331 with open(bytecode_path, 'rb') as bytecode_file:
332 bytecode_file.seek(4)
333 self.assertEqual(bytecode_file.read(4), source_timestamp)
334
Brett Cannone52c9192009-11-07 23:55:05 +0000335 # [bytecode read-only]
336 @source_util.writes_bytecode_files
337 def test_read_only_bytecode(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000338 # When bytecode is read-only but should be rewritten, fail silently.
Brett Cannone52c9192009-11-07 23:55:05 +0000339 with source_util.create_modules('_temp') as mapping:
340 # Create bytecode that will need to be re-created.
341 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000342 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannone52c9192009-11-07 23:55:05 +0000343 with open(bytecode_path, 'r+b') as bytecode_file:
344 bytecode_file.seek(0)
345 bytecode_file.write(b'\x00\x00\x00\x00')
346 # Make the bytecode read-only.
347 os.chmod(bytecode_path,
348 stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
349 try:
350 # Should not raise IOError!
351 self.import_(mapping['_temp'], '_temp')
352 finally:
353 # Make writable for eventual clean-up.
354 os.chmod(bytecode_path, stat.S_IWUSR)
355
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000356
Brett Cannon61b14252010-07-03 21:48:25 +0000357class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
358
359 loader = _bootstrap._SourcelessFileLoader
360
361 def test_empty_file(self):
362 def test(name, mapping, bytecode_path):
363 with self.assertRaises(ImportError):
364 self.import_(bytecode_path, name)
365
366 self._test_empty_file(test, del_source=True)
367
368 def test_partial_magic(self):
369 def test(name, mapping, bytecode_path):
370 with self.assertRaises(ImportError):
371 self.import_(bytecode_path, name)
372 self._test_partial_magic(test, del_source=True)
373
374 def test_magic_only(self):
375 def test(name, mapping, bytecode_path):
376 with self.assertRaises(EOFError):
377 self.import_(bytecode_path, name)
378
379 self._test_magic_only(test, del_source=True)
380
381 def test_bad_magic(self):
382 def test(name, mapping, bytecode_path):
383 with self.assertRaises(ImportError):
384 self.import_(bytecode_path, name)
385
386 self._test_bad_magic(test, del_source=True)
387
388 def test_partial_timestamp(self):
389 def test(name, mapping, bytecode_path):
390 with self.assertRaises(EOFError):
391 self.import_(bytecode_path, name)
392
393 self._test_partial_timestamp(test, del_source=True)
394
395 def test_no_marshal(self):
396 self._test_no_marshal(del_source=True)
397
398 def test_non_code_marshal(self):
399 self._test_non_code_marshal(del_source=True)
400
401
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000402def test_main():
403 from test.support import run_unittest
Brett Cannon61b14252010-07-03 21:48:25 +0000404 run_unittest(SimpleTest,
405 SourceLoaderBadBytecodeTest,
406 SourcelessLoaderBadBytecodeTest
Brett Cannon186335b2010-08-22 22:11:06 +0000407 )
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000408
409
410if __name__ == '__main__':
411 test_main()