blob: 20280927273924738c3c133dfedf0b720a7fbb7f [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
Brett Cannon1262e7c2009-05-11 01:47:11 +0000259 @source_util.writes_bytecode_files
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000260 def test_bad_magic(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000261 # When the magic number is different, the bytecode should be
262 # regenerated.
Brett Cannon61b14252010-07-03 21:48:25 +0000263 def test(name, mapping, bytecode_path):
264 self.import_(mapping[name], name)
265 with open(bytecode_path, 'rb') as bytecode_file:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000266 self.assertEqual(bytecode_file.read(4), imp.get_magic())
267
Brett Cannon61b14252010-07-03 21:48:25 +0000268 self._test_bad_magic(test)
269
270 @source_util.writes_bytecode_files
271 def test_partial_timestamp(self):
272 # When the timestamp is partial, regenerate the .pyc, else
273 # raise EOFError.
274 def test(name, mapping, bc_path):
275 self.import_(mapping[name], name)
276 with open(bc_path, 'rb') as file:
277 self.assertGreater(len(file.read()), 8)
278
279 @source_util.writes_bytecode_files
280 def test_no_marshal(self):
281 # When there is only the magic number and timestamp, raise EOFError.
282 self._test_no_marshal()
283
284 @source_util.writes_bytecode_files
285 def test_non_code_marshal(self):
286 self._test_non_code_marshal()
287 # XXX ImportError when sourceless
288
289 # [bad marshal]
290 @source_util.writes_bytecode_files
291 def test_bad_marshal(self):
292 # Bad marshal data should raise a ValueError.
293 self._test_bad_marshal()
294
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000295 # [bad timestamp]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000296 @source_util.writes_bytecode_files
Brett Cannon61b14252010-07-03 21:48:25 +0000297 def test_old_timestamp(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000298 # When the timestamp is older than the source, bytecode should be
299 # regenerated.
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000300 zeros = b'\x00\x00\x00\x00'
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000301 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000302 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000303 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000304 with open(bytecode_path, 'r+b') as bytecode_file:
305 bytecode_file.seek(4)
306 bytecode_file.write(zeros)
307 self.import_(mapping['_temp'], '_temp')
308 source_mtime = os.path.getmtime(mapping['_temp'])
309 source_timestamp = importlib._w_long(source_mtime)
310 with open(bytecode_path, 'rb') as bytecode_file:
311 bytecode_file.seek(4)
312 self.assertEqual(bytecode_file.read(4), source_timestamp)
313
Brett Cannone52c9192009-11-07 23:55:05 +0000314 # [bytecode read-only]
315 @source_util.writes_bytecode_files
316 def test_read_only_bytecode(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000317 # When bytecode is read-only but should be rewritten, fail silently.
Brett Cannone52c9192009-11-07 23:55:05 +0000318 with source_util.create_modules('_temp') as mapping:
319 # Create bytecode that will need to be re-created.
320 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000321 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannone52c9192009-11-07 23:55:05 +0000322 with open(bytecode_path, 'r+b') as bytecode_file:
323 bytecode_file.seek(0)
324 bytecode_file.write(b'\x00\x00\x00\x00')
325 # Make the bytecode read-only.
326 os.chmod(bytecode_path,
327 stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
328 try:
329 # Should not raise IOError!
330 self.import_(mapping['_temp'], '_temp')
331 finally:
332 # Make writable for eventual clean-up.
333 os.chmod(bytecode_path, stat.S_IWUSR)
334
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000335
Brett Cannon61b14252010-07-03 21:48:25 +0000336class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
337
338 loader = _bootstrap._SourcelessFileLoader
339
340 def test_empty_file(self):
341 def test(name, mapping, bytecode_path):
342 with self.assertRaises(ImportError):
343 self.import_(bytecode_path, name)
344
345 self._test_empty_file(test, del_source=True)
346
347 def test_partial_magic(self):
348 def test(name, mapping, bytecode_path):
349 with self.assertRaises(ImportError):
350 self.import_(bytecode_path, name)
351 self._test_partial_magic(test, del_source=True)
352
353 def test_magic_only(self):
354 def test(name, mapping, bytecode_path):
355 with self.assertRaises(EOFError):
356 self.import_(bytecode_path, name)
357
358 self._test_magic_only(test, del_source=True)
359
360 def test_bad_magic(self):
361 def test(name, mapping, bytecode_path):
362 with self.assertRaises(ImportError):
363 self.import_(bytecode_path, name)
364
365 self._test_bad_magic(test, del_source=True)
366
367 def test_partial_timestamp(self):
368 def test(name, mapping, bytecode_path):
369 with self.assertRaises(EOFError):
370 self.import_(bytecode_path, name)
371
372 self._test_partial_timestamp(test, del_source=True)
373
374 def test_no_marshal(self):
375 self._test_no_marshal(del_source=True)
376
377 def test_non_code_marshal(self):
378 self._test_non_code_marshal(del_source=True)
379
380
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000381def test_main():
382 from test.support import run_unittest
Brett Cannon61b14252010-07-03 21:48:25 +0000383 run_unittest(SimpleTest,
384 SourceLoaderBadBytecodeTest,
385 SourcelessLoaderBadBytecodeTest
Brett Cannon186335b2010-08-22 22:11:06 +0000386 )
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000387
388
389if __name__ == '__main__':
390 test_main()