blob: ce6f5d47a3181f797ad25bc9c5ecb758224137bd [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 Cannone52c9192009-11-07 23:55:05 +000011import stat
Brett Cannon23cbd8a2009-01-18 00:24:28 +000012import sys
13import unittest
14
Barry Warsaw04b56842010-05-18 14:15:20 +000015from test.support import make_legacy_pyc
16
Brett Cannon23cbd8a2009-01-18 00:24:28 +000017
18class SimpleTest(unittest.TestCase):
19
20 """Should have no issue importing a source module [basic]. And if there is
21 a syntax error, it should raise a SyntaxError [syntax error].
22
23 """
24
25 # [basic]
Brett Cannon30b047d2009-02-01 02:05:11 +000026 def test_module(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000027 with source_util.create_modules('_temp') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000028 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon30b047d2009-02-01 02:05:11 +000029 module = loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000030 self.assertTrue('_temp' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000031 check = {'__name__': '_temp', '__file__': mapping['_temp'],
Brett Cannon06c9d962009-02-07 01:52:25 +000032 '__package__': ''}
Brett Cannon30b047d2009-02-01 02:05:11 +000033 for attr, value in check.items():
34 self.assertEqual(getattr(module, attr), value)
35
36 def test_package(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000037 with source_util.create_modules('_pkg.__init__') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000038 loader = _bootstrap._SourceFileLoader('_pkg',
39 mapping['_pkg.__init__'])
Brett Cannon30b047d2009-02-01 02:05:11 +000040 module = loader.load_module('_pkg')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000041 self.assertTrue('_pkg' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000042 check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'],
43 '__path__': [os.path.dirname(mapping['_pkg.__init__'])],
44 '__package__': '_pkg'}
45 for attr, value in check.items():
46 self.assertEqual(getattr(module, attr), value)
47
48
49 def test_lacking_parent(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000050 with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000051 loader = _bootstrap._SourceFileLoader('_pkg.mod',
52 mapping['_pkg.mod'])
Brett Cannon30b047d2009-02-01 02:05:11 +000053 module = loader.load_module('_pkg.mod')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000054 self.assertTrue('_pkg.mod' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000055 check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'],
56 '__package__': '_pkg'}
57 for attr, value in check.items():
58 self.assertEqual(getattr(module, attr), value)
59
60 def fake_mtime(self, fxn):
61 """Fake mtime to always be higher than expected."""
62 return lambda name: fxn(name) + 1
63
64 def test_module_reuse(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000065 with source_util.create_modules('_temp') as mapping:
Brett Cannon61b14252010-07-03 21:48:25 +000066 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon30b047d2009-02-01 02:05:11 +000067 module = loader.load_module('_temp')
68 module_id = id(module)
69 module_dict_id = id(module.__dict__)
70 with open(mapping['_temp'], 'w') as file:
71 file.write("testing_var = 42\n")
72 # For filesystems where the mtime is only to a second granularity,
73 # everything that has happened above can be too fast;
74 # force an mtime on the source that is guaranteed to be different
75 # than the original mtime.
Brett Cannon61b14252010-07-03 21:48:25 +000076 loader.path_mtime = self.fake_mtime(loader.path_mtime)
Brett Cannon30b047d2009-02-01 02:05:11 +000077 module = loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000078 self.assertTrue('testing_var' in module.__dict__,
Brett Cannon30b047d2009-02-01 02:05:11 +000079 "'testing_var' not in "
80 "{0}".format(list(module.__dict__.keys())))
81 self.assertEqual(module, sys.modules['_temp'])
82 self.assertEqual(id(module), module_id)
83 self.assertEqual(id(module.__dict__), module_dict_id)
84
85 def test_state_after_failure(self):
86 # A failed reload should leave the original module intact.
87 attributes = ('__file__', '__path__', '__package__')
88 value = '<test>'
89 name = '_temp'
Brett Cannon4ee2cda2009-02-01 03:08:31 +000090 with source_util.create_modules(name) as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000091 orig_module = imp.new_module(name)
92 for attr in attributes:
93 setattr(orig_module, attr, value)
94 with open(mapping[name], 'w') as file:
95 file.write('+++ bad syntax +++')
Brett Cannon61b14252010-07-03 21:48:25 +000096 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon2153dc02009-08-27 23:49:21 +000097 with self.assertRaises(SyntaxError):
98 loader.load_module(name)
Brett Cannon30b047d2009-02-01 02:05:11 +000099 for attr in attributes:
100 self.assertEqual(getattr(orig_module, attr), value)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000101
102 # [syntax error]
103 def test_bad_syntax(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000104 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000105 with open(mapping['_temp'], 'w') as file:
106 file.write('=')
Brett Cannon61b14252010-07-03 21:48:25 +0000107 loader = _bootstrap._SourceFileLoader('_temp', mapping['_temp'])
Brett Cannon2153dc02009-08-27 23:49:21 +0000108 with self.assertRaises(SyntaxError):
109 loader.load_module('_temp')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000110 self.assertTrue('_temp' not in sys.modules)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000111
Brett Cannond71bed32010-07-03 22:18:47 +0000112 def test_file_from_empty_string_dir(self):
113 # Loading a module found from an empty string entry on sys.path should
114 # not only work, but keep all attributes relative.
115 with open('_temp.py', 'w') as file:
116 file.write("# test file for importlib")
117 try:
118 with util.uncache('_temp'):
119 loader = _bootstrap._SourceFileLoader('_temp', '_temp.py')
120 mod = loader.load_module('_temp')
121 self.assertEqual('_temp.py', mod.__file__)
122 self.assertEqual(imp.cache_from_source('_temp.py'),
123 mod.__cached__)
124
125 finally:
126 os.unlink('_temp.py')
127
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000128
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000129class BadBytecodeTest(unittest.TestCase):
130
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000131 def import_(self, file, module_name):
Brett Cannon61b14252010-07-03 21:48:25 +0000132 loader = self.loader(module_name, file)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000133 module = loader.load_module(module_name)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue(module_name in sys.modules)
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000135
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000136 def manipulate_bytecode(self, name, mapping, manipulator, *,
137 del_source=False):
138 """Manipulate the bytecode of a module by passing it into a callable
139 that returns what to use as the new bytecode."""
140 try:
141 del sys.modules['_temp']
142 except KeyError:
143 pass
144 py_compile.compile(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000145 if not del_source:
146 bytecode_path = imp.cache_from_source(mapping[name])
147 else:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000148 os.unlink(mapping[name])
Brett Cannon61b14252010-07-03 21:48:25 +0000149 bytecode_path = make_legacy_pyc(mapping[name])
150 if manipulator:
151 with open(bytecode_path, 'rb') as file:
152 bc = file.read()
153 new_bc = manipulator(bc)
154 with open(bytecode_path, 'wb') as file:
155 if new_bc is not None:
156 file.write(new_bc)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000157 return bytecode_path
158
Brett Cannon61b14252010-07-03 21:48:25 +0000159 def _test_empty_file(self, test, *, del_source=False):
160 with source_util.create_modules('_temp') as mapping:
161 bc_path = self.manipulate_bytecode('_temp', mapping,
162 lambda bc: b'',
163 del_source=del_source)
164 test('_temp', mapping, bc_path)
165
166 @source_util.writes_bytecode_files
167 def _test_partial_magic(self, test, *, del_source=False):
168 # When their are less than 4 bytes to a .pyc, regenerate it if
169 # possible, else raise ImportError.
170 with source_util.create_modules('_temp') as mapping:
171 bc_path = self.manipulate_bytecode('_temp', mapping,
172 lambda bc: bc[:3],
173 del_source=del_source)
174 test('_temp', mapping, bc_path)
175
176 def _test_magic_only(self, test, *, del_source=False):
177 with source_util.create_modules('_temp') as mapping:
178 bc_path = self.manipulate_bytecode('_temp', mapping,
179 lambda bc: bc[:4],
180 del_source=del_source)
181 test('_temp', mapping, bc_path)
182
183 def _test_partial_timestamp(self, test, *, del_source=False):
184 with source_util.create_modules('_temp') as mapping:
185 bc_path = self.manipulate_bytecode('_temp', mapping,
186 lambda bc: bc[:7],
187 del_source=del_source)
188 test('_temp', mapping, bc_path)
189
190 def _test_no_marshal(self, *, del_source=False):
191 with source_util.create_modules('_temp') as mapping:
192 bc_path = self.manipulate_bytecode('_temp', mapping,
193 lambda bc: bc[:8],
194 del_source=del_source)
195 file_path = mapping['_temp'] if not del_source else bc_path
196 with self.assertRaises(EOFError):
197 self.import_(file_path, '_temp')
198
199 def _test_non_code_marshal(self, *, del_source=False):
200 with source_util.create_modules('_temp') as mapping:
201 bytecode_path = self.manipulate_bytecode('_temp', mapping,
202 lambda bc: bc[:8] + marshal.dumps(b'abcd'),
203 del_source=del_source)
204 file_path = mapping['_temp'] if not del_source else bytecode_path
205 with self.assertRaises(ImportError):
206 self.import_(file_path, '_temp')
207
208 def _test_bad_marshal(self, *, del_source=False):
209 with source_util.create_modules('_temp') as mapping:
210 bytecode_path = self.manipulate_bytecode('_temp', mapping,
211 lambda bc: bc[:8] + b'<test>',
212 del_source=del_source)
213 file_path = mapping['_temp'] if not del_source else bytecode_path
214 with self.assertRaises(ValueError):
215 self.import_(file_path, '_temp')
216
217 def _test_bad_magic(self, test, *, del_source=False):
218 with source_util.create_modules('_temp') as mapping:
219 bc_path = self.manipulate_bytecode('_temp', mapping,
220 lambda bc: b'\x00\x00\x00\x00' + bc[4:])
221 test('_temp', mapping, bc_path)
222
223
224class SourceLoaderBadBytecodeTest(BadBytecodeTest):
225
226 loader = _bootstrap._SourceFileLoader
227
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000228 @source_util.writes_bytecode_files
229 def test_empty_file(self):
230 # When a .pyc is empty, regenerate it if possible, else raise
231 # ImportError.
Brett Cannon61b14252010-07-03 21:48:25 +0000232 def test(name, mapping, bytecode_path):
233 self.import_(mapping[name], name)
234 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000235 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000236
Brett Cannon61b14252010-07-03 21:48:25 +0000237 self._test_empty_file(test)
238
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000239 def test_partial_magic(self):
Brett Cannon61b14252010-07-03 21:48:25 +0000240 def test(name, mapping, bytecode_path):
241 self.import_(mapping[name], name)
242 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000243 self.assertGreater(len(file.read()), 8)
Brett Cannon61b14252010-07-03 21:48:25 +0000244
245 self._test_partial_magic(test)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000246
247 @source_util.writes_bytecode_files
248 def test_magic_only(self):
249 # When there is only the magic number, regenerate the .pyc if possible,
250 # else raise EOFError.
Brett Cannon61b14252010-07-03 21:48:25 +0000251 def test(name, mapping, bytecode_path):
252 self.import_(mapping[name], name)
253 with open(bytecode_path, 'rb') as file:
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000254 self.assertGreater(len(file.read()), 8)
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000255
Brett Cannon1262e7c2009-05-11 01:47:11 +0000256 @source_util.writes_bytecode_files
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000257 def test_bad_magic(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000258 # When the magic number is different, the bytecode should be
259 # regenerated.
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 bytecode_file:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000263 self.assertEqual(bytecode_file.read(4), imp.get_magic())
264
Brett Cannon61b14252010-07-03 21:48:25 +0000265 self._test_bad_magic(test)
266
267 @source_util.writes_bytecode_files
268 def test_partial_timestamp(self):
269 # When the timestamp is partial, regenerate the .pyc, else
270 # raise EOFError.
271 def test(name, mapping, bc_path):
272 self.import_(mapping[name], name)
273 with open(bc_path, 'rb') as file:
274 self.assertGreater(len(file.read()), 8)
275
276 @source_util.writes_bytecode_files
277 def test_no_marshal(self):
278 # When there is only the magic number and timestamp, raise EOFError.
279 self._test_no_marshal()
280
281 @source_util.writes_bytecode_files
282 def test_non_code_marshal(self):
283 self._test_non_code_marshal()
284 # XXX ImportError when sourceless
285
286 # [bad marshal]
287 @source_util.writes_bytecode_files
288 def test_bad_marshal(self):
289 # Bad marshal data should raise a ValueError.
290 self._test_bad_marshal()
291
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000292 # [bad timestamp]
Brett Cannon1262e7c2009-05-11 01:47:11 +0000293 @source_util.writes_bytecode_files
Brett Cannon61b14252010-07-03 21:48:25 +0000294 def test_old_timestamp(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000295 # When the timestamp is older than the source, bytecode should be
296 # regenerated.
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000297 zeros = b'\x00\x00\x00\x00'
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000298 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000299 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000300 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000301 with open(bytecode_path, 'r+b') as bytecode_file:
302 bytecode_file.seek(4)
303 bytecode_file.write(zeros)
304 self.import_(mapping['_temp'], '_temp')
305 source_mtime = os.path.getmtime(mapping['_temp'])
306 source_timestamp = importlib._w_long(source_mtime)
307 with open(bytecode_path, 'rb') as bytecode_file:
308 bytecode_file.seek(4)
309 self.assertEqual(bytecode_file.read(4), source_timestamp)
310
Brett Cannone52c9192009-11-07 23:55:05 +0000311 # [bytecode read-only]
312 @source_util.writes_bytecode_files
313 def test_read_only_bytecode(self):
Brett Cannon9b3e15f2010-02-19 16:01:06 +0000314 # When bytecode is read-only but should be rewritten, fail silently.
Brett Cannone52c9192009-11-07 23:55:05 +0000315 with source_util.create_modules('_temp') as mapping:
316 # Create bytecode that will need to be re-created.
317 py_compile.compile(mapping['_temp'])
Barry Warsaw28a691b2010-04-17 00:19:56 +0000318 bytecode_path = imp.cache_from_source(mapping['_temp'])
Brett Cannone52c9192009-11-07 23:55:05 +0000319 with open(bytecode_path, 'r+b') as bytecode_file:
320 bytecode_file.seek(0)
321 bytecode_file.write(b'\x00\x00\x00\x00')
322 # Make the bytecode read-only.
323 os.chmod(bytecode_path,
324 stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
325 try:
326 # Should not raise IOError!
327 self.import_(mapping['_temp'], '_temp')
328 finally:
329 # Make writable for eventual clean-up.
330 os.chmod(bytecode_path, stat.S_IWUSR)
331
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000332
Brett Cannon61b14252010-07-03 21:48:25 +0000333class SourcelessLoaderBadBytecodeTest(BadBytecodeTest):
334
335 loader = _bootstrap._SourcelessFileLoader
336
337 def test_empty_file(self):
338 def test(name, mapping, bytecode_path):
339 with self.assertRaises(ImportError):
340 self.import_(bytecode_path, name)
341
342 self._test_empty_file(test, del_source=True)
343
344 def test_partial_magic(self):
345 def test(name, mapping, bytecode_path):
346 with self.assertRaises(ImportError):
347 self.import_(bytecode_path, name)
348 self._test_partial_magic(test, del_source=True)
349
350 def test_magic_only(self):
351 def test(name, mapping, bytecode_path):
352 with self.assertRaises(EOFError):
353 self.import_(bytecode_path, name)
354
355 self._test_magic_only(test, del_source=True)
356
357 def test_bad_magic(self):
358 def test(name, mapping, bytecode_path):
359 with self.assertRaises(ImportError):
360 self.import_(bytecode_path, name)
361
362 self._test_bad_magic(test, del_source=True)
363
364 def test_partial_timestamp(self):
365 def test(name, mapping, bytecode_path):
366 with self.assertRaises(EOFError):
367 self.import_(bytecode_path, name)
368
369 self._test_partial_timestamp(test, del_source=True)
370
371 def test_no_marshal(self):
372 self._test_no_marshal(del_source=True)
373
374 def test_non_code_marshal(self):
375 self._test_non_code_marshal(del_source=True)
376
377
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000378def test_main():
379 from test.support import run_unittest
Brett Cannon61b14252010-07-03 21:48:25 +0000380 run_unittest(SimpleTest,
381 SourceLoaderBadBytecodeTest,
382 SourcelessLoaderBadBytecodeTest
383 )
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000384
385
386if __name__ == '__main__':
387 test_main()