blob: de1c4d8a014aae1cec55c588791a5e34a400ecb8 [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
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000131
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000132class BadBytecodeTest(unittest.TestCase):
133
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000134 def import_(self, file, module_name):
Brett Cannon61b14252010-07-03 21:48:25 +0000135 loader = self.loader(module_name, file)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000136 module = loader.load_module(module_name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000137 self.assertTrue(module_name in sys.modules)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000138
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000139 def manipulate_bytecode(self, name, mapping, manipulator, *,
140 del_source=False):
141 """Manipulate the bytecode of a module by passing it into a callable
142 that returns what to use as the new bytecode."""
143 try:
144 del sys.modules['_temp']
145 except KeyError:
146 pass
147 py_compile.compile(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000148 if not del_source:
149 bytecode_path = imp.cache_from_source(mapping[name])
150 else:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000151 os.unlink(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000152 bytecode_path = make_legacy_pyc(mapping[name])
153 if manipulator:
154 with open(bytecode_path, 'rb') as file:
155 bc = file.read()
156 new_bc = manipulator(bc)
157 with open(bytecode_path, 'wb') as file:
158 if new_bc is not None:
159 file.write(new_bc)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000160 return bytecode_path
161
Brett Cannon61b14252010-07-03 21:48:25 +0000162 def _test_empty_file(self, test, *, del_source=False):
163 with source_util.create_modules('_temp') as mapping:
164 bc_path = self.manipulate_bytecode('_temp', mapping,
165 lambda bc: b'',
166 del_source=del_source)
167 test('_temp', mapping, bc_path)
168
169 @source_util.writes_bytecode_files
170 def _test_partial_magic(self, test, *, del_source=False):
171 # When their are less than 4 bytes to a .pyc, regenerate it if
172 # possible, else raise ImportError.
173 with source_util.create_modules('_temp') as mapping:
174 bc_path = self.manipulate_bytecode('_temp', mapping,
175 lambda bc: bc[:3],
176 del_source=del_source)
177 test('_temp', mapping, bc_path)
178
179 def _test_magic_only(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: bc[:4],
183 del_source=del_source)
184 test('_temp', mapping, bc_path)
185
186 def _test_partial_timestamp(self, test, *, del_source=False):
187 with source_util.create_modules('_temp') as mapping:
188 bc_path = self.manipulate_bytecode('_temp', mapping,
189 lambda bc: bc[:7],
190 del_source=del_source)
191 test('_temp', mapping, bc_path)
192
193 def _test_no_marshal(self, *, del_source=False):
194 with source_util.create_modules('_temp') as mapping:
195 bc_path = self.manipulate_bytecode('_temp', mapping,
196 lambda bc: bc[:8],
197 del_source=del_source)
198 file_path = mapping['_temp'] if not del_source else bc_path
199 with self.assertRaises(EOFError):
200 self.import_(file_path, '_temp')
201
202 def _test_non_code_marshal(self, *, del_source=False):
203 with source_util.create_modules('_temp') as mapping:
204 bytecode_path = self.manipulate_bytecode('_temp', mapping,
205 lambda bc: bc[:8] + marshal.dumps(b'abcd'),
206 del_source=del_source)
207 file_path = mapping['_temp'] if not del_source else bytecode_path
208 with self.assertRaises(ImportError):
209 self.import_(file_path, '_temp')
210
211 def _test_bad_marshal(self, *, del_source=False):
212 with source_util.create_modules('_temp') as mapping:
213 bytecode_path = self.manipulate_bytecode('_temp', mapping,
214 lambda bc: bc[:8] + b'<test>',
215 del_source=del_source)
216 file_path = mapping['_temp'] if not del_source else bytecode_path
Vinay Sajip5bdae3b2011-07-02 16:42:47 +0100217 with self.assertRaises(EOFError):
Brett Cannon61b14252010-07-03 21:48:25 +0000218 self.import_(file_path, '_temp')
219
220 def _test_bad_magic(self, test, *, del_source=False):
221 with source_util.create_modules('_temp') as mapping:
222 bc_path = self.manipulate_bytecode('_temp', mapping,
223 lambda bc: b'\x00\x00\x00\x00' + bc[4:])
224 test('_temp', mapping, bc_path)
225
226
227class SourceLoaderBadBytecodeTest(BadBytecodeTest):
228
229 loader = _bootstrap._SourceFileLoader
230
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000231 @source_util.writes_bytecode_files
232 def test_empty_file(self):
233 # When a .pyc is empty, regenerate it if possible, else raise
234 # ImportError.
Brett Cannon61b14252010-07-03 21:48:25 +0000235 def test(name, mapping, bytecode_path):
236 self.import_(mapping[name], name)
237 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000238 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000239
Brett Cannon61b14252010-07-03 21:48:25 +0000240 self._test_empty_file(test)
241
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000242 def test_partial_magic(self):
Brett Cannon61b14252010-07-03 21:48:25 +0000243 def test(name, mapping, bytecode_path):
244 self.import_(mapping[name], name)
245 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000246 self.assertGreater(len(file.read()), 8)
Brett Cannon61b14252010-07-03 21:48:25 +0000247
248 self._test_partial_magic(test)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000249
250 @source_util.writes_bytecode_files
251 def test_magic_only(self):
252 # When there is only the magic number, regenerate the .pyc if possible,
253 # else raise EOFError.
Brett Cannon61b14252010-07-03 21:48:25 +0000254 def test(name, mapping, bytecode_path):
255 self.import_(mapping[name], name)
256 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000257 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000258
Antoine Pitrou7c9907e2011-12-30 21:25:15 +0100259 self._test_magic_only(test)
260
Brett Cannon1262e7c2009-05-11 01:47:11 +0000261 @source_util.writes_bytecode_files
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000262 def test_bad_magic(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000263 # When the magic number is different, the bytecode should be
264 # regenerated.
Brett Cannon61b14252010-07-03 21:48:25 +0000265 def test(name, mapping, bytecode_path):
266 self.import_(mapping[name], name)
267 with open(bytecode_path, 'rb') as bytecode_file:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000268 self.assertEqual(bytecode_file.read(4), imp.get_magic())
269
Brett Cannon61b14252010-07-03 21:48:25 +0000270 self._test_bad_magic(test)
271
272 @source_util.writes_bytecode_files
273 def test_partial_timestamp(self):
274 # When the timestamp is partial, regenerate the .pyc, else
275 # raise EOFError.
276 def test(name, mapping, bc_path):
277 self.import_(mapping[name], name)
278 with open(bc_path, 'rb') as file:
279 self.assertGreater(len(file.read()), 8)
280
Antoine Pitrou7c9907e2011-12-30 21:25:15 +0100281 self._test_partial_timestamp(test)
282
Brett Cannon61b14252010-07-03 21:48:25 +0000283 @source_util.writes_bytecode_files
284 def test_no_marshal(self):
285 # When there is only the magic number and timestamp, raise EOFError.
286 self._test_no_marshal()
287
288 @source_util.writes_bytecode_files
289 def test_non_code_marshal(self):
290 self._test_non_code_marshal()
291 # XXX ImportError when sourceless
292
293 # [bad marshal]
294 @source_util.writes_bytecode_files
295 def test_bad_marshal(self):
296 # Bad marshal data should raise a ValueError.
297 self._test_bad_marshal()
298
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000299 # [bad timestamp]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000300 @source_util.writes_bytecode_files
Brett Cannon61b14252010-07-03 21:48:25 +0000301 def test_old_timestamp(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000302 # When the timestamp is older than the source, bytecode should be
303 # regenerated.
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000304 zeros = b'\x00\x00\x00\x00'
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000305 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000306 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000307 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000308 with open(bytecode_path, 'r+b') as bytecode_file:
309 bytecode_file.seek(4)
310 bytecode_file.write(zeros)
311 self.import_(mapping['_temp'], '_temp')
312 source_mtime = os.path.getmtime(mapping['_temp'])
313 source_timestamp = importlib._w_long(source_mtime)
314 with open(bytecode_path, 'rb') as bytecode_file:
315 bytecode_file.seek(4)
316 self.assertEqual(bytecode_file.read(4), source_timestamp)
317
Brett Cannone52c9192009-11-07 23:55:05 +0000318 # [bytecode read-only]
319 @source_util.writes_bytecode_files
320 def test_read_only_bytecode(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000321 # When bytecode is read-only but should be rewritten, fail silently.
Brett Cannone52c9192009-11-07 23:55:05 +0000322 with source_util.create_modules('_temp') as mapping:
323 # Create bytecode that will need to be re-created.
324 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000325 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannone52c9192009-11-07 23:55:05 +0000326 with open(bytecode_path, 'r+b') as bytecode_file:
327 bytecode_file.seek(0)
328 bytecode_file.write(b'\x00\x00\x00\x00')
329 # Make the bytecode read-only.
330 os.chmod(bytecode_path,
331 stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
332 try:
333 # Should not raise IOError!
334 self.import_(mapping['_temp'], '_temp')
335 finally:
336 # Make writable for eventual clean-up.
337 os.chmod(bytecode_path, stat.S_IWUSR)
338
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000339
Brett Cannon61b14252010-07-03 21:48:25 +0000340class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
341
342 loader = _bootstrap._SourcelessFileLoader
343
344 def test_empty_file(self):
345 def test(name, mapping, bytecode_path):
346 with self.assertRaises(ImportError):
347 self.import_(bytecode_path, name)
348
349 self._test_empty_file(test, del_source=True)
350
351 def test_partial_magic(self):
352 def test(name, mapping, bytecode_path):
353 with self.assertRaises(ImportError):
354 self.import_(bytecode_path, name)
355 self._test_partial_magic(test, del_source=True)
356
357 def test_magic_only(self):
358 def test(name, mapping, bytecode_path):
359 with self.assertRaises(EOFError):
360 self.import_(bytecode_path, name)
361
362 self._test_magic_only(test, del_source=True)
363
364 def test_bad_magic(self):
365 def test(name, mapping, bytecode_path):
366 with self.assertRaises(ImportError):
367 self.import_(bytecode_path, name)
368
369 self._test_bad_magic(test, del_source=True)
370
371 def test_partial_timestamp(self):
372 def test(name, mapping, bytecode_path):
373 with self.assertRaises(EOFError):
374 self.import_(bytecode_path, name)
375
376 self._test_partial_timestamp(test, del_source=True)
377
378 def test_no_marshal(self):
379 self._test_no_marshal(del_source=True)
380
381 def test_non_code_marshal(self):
382 self._test_non_code_marshal(del_source=True)
383
384
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000385def test_main():
386 from test.support import run_unittest
Brett Cannon61b14252010-07-03 21:48:25 +0000387 run_unittest(SimpleTest,
388 SourceLoaderBadBytecodeTest,
389 SourcelessLoaderBadBytecodeTest
Brett Cannon186335b2010-08-22 22:11:06 +0000390 )
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000391
392
393if __name__ == '__main__':
394 test_main()