blob: 4ca9af1504b5ca6576479a325f63f0fe8c773b2b [file] [log] [blame]
Brett Cannon23cbd8a2009-01-18 00:24:28 +00001import importlib
Brett Cannon30b047d2009-02-01 02:05:11 +00002from .. import abc
Brett Cannon4ee2cda2009-02-01 03:08:31 +00003from . import util as source_util
Brett Cannon23cbd8a2009-01-18 00:24:28 +00004
5import imp
6import os
7import py_compile
8import sys
9import unittest
10
11
12class SimpleTest(unittest.TestCase):
13
14 """Should have no issue importing a source module [basic]. And if there is
15 a syntax error, it should raise a SyntaxError [syntax error].
16
17 """
18
19 # [basic]
Brett Cannon30b047d2009-02-01 02:05:11 +000020 def test_module(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000021 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +000022 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
Brett Cannon30b047d2009-02-01 02:05:11 +000023 module = loader.load_module('_temp')
Brett Cannon23cbd8a2009-01-18 00:24:28 +000024 self.assert_('_temp' in sys.modules)
Brett Cannon30b047d2009-02-01 02:05:11 +000025 check = {'__name__': '_temp', '__file__': mapping['_temp'],
Brett Cannon06c9d962009-02-07 01:52:25 +000026 '__package__': ''}
Brett Cannon30b047d2009-02-01 02:05:11 +000027 for attr, value in check.items():
28 self.assertEqual(getattr(module, attr), value)
29
30 def test_package(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000031 with source_util.create_modules('_pkg.__init__') as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000032 loader = importlib._PyFileLoader('_pkg', mapping['_pkg.__init__'],
33 True)
34 module = loader.load_module('_pkg')
35 self.assert_('_pkg' in sys.modules)
36 check = {'__name__': '_pkg', '__file__': mapping['_pkg.__init__'],
37 '__path__': [os.path.dirname(mapping['_pkg.__init__'])],
38 '__package__': '_pkg'}
39 for attr, value in check.items():
40 self.assertEqual(getattr(module, attr), value)
41
42
43 def test_lacking_parent(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000044 with source_util.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000045 loader = importlib._PyFileLoader('_pkg.mod', mapping['_pkg.mod'],
46 False)
47 module = loader.load_module('_pkg.mod')
48 self.assert_('_pkg.mod' in sys.modules)
49 check = {'__name__': '_pkg.mod', '__file__': mapping['_pkg.mod'],
50 '__package__': '_pkg'}
51 for attr, value in check.items():
52 self.assertEqual(getattr(module, attr), value)
53
54 def fake_mtime(self, fxn):
55 """Fake mtime to always be higher than expected."""
56 return lambda name: fxn(name) + 1
57
58 def test_module_reuse(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000059 with source_util.create_modules('_temp') as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000060 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
61 module = loader.load_module('_temp')
62 module_id = id(module)
63 module_dict_id = id(module.__dict__)
64 with open(mapping['_temp'], 'w') as file:
65 file.write("testing_var = 42\n")
66 # For filesystems where the mtime is only to a second granularity,
67 # everything that has happened above can be too fast;
68 # force an mtime on the source that is guaranteed to be different
69 # than the original mtime.
70 loader.source_mtime = self.fake_mtime(loader.source_mtime)
71 module = loader.load_module('_temp')
72 self.assert_('testing_var' in module.__dict__,
73 "'testing_var' not in "
74 "{0}".format(list(module.__dict__.keys())))
75 self.assertEqual(module, sys.modules['_temp'])
76 self.assertEqual(id(module), module_id)
77 self.assertEqual(id(module.__dict__), module_dict_id)
78
79 def test_state_after_failure(self):
80 # A failed reload should leave the original module intact.
81 attributes = ('__file__', '__path__', '__package__')
82 value = '<test>'
83 name = '_temp'
Brett Cannon4ee2cda2009-02-01 03:08:31 +000084 with source_util.create_modules(name) as mapping:
Brett Cannon30b047d2009-02-01 02:05:11 +000085 orig_module = imp.new_module(name)
86 for attr in attributes:
87 setattr(orig_module, attr, value)
88 with open(mapping[name], 'w') as file:
89 file.write('+++ bad syntax +++')
90 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
91 self.assertRaises(SyntaxError, loader.load_module, name)
92 for attr in attributes:
93 self.assertEqual(getattr(orig_module, attr), value)
Brett Cannon23cbd8a2009-01-18 00:24:28 +000094
95 # [syntax error]
96 def test_bad_syntax(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +000097 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +000098 with open(mapping['_temp'], 'w') as file:
99 file.write('=')
100 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
101 self.assertRaises(SyntaxError, loader.load_module, '_temp')
102 self.assert_('_temp' not in sys.modules)
103
104
105class DontWriteBytecodeTest(unittest.TestCase):
106
107 """If sys.dont_write_bytcode is true then no bytecode should be created."""
108
109 def tearDown(self):
110 sys.dont_write_bytecode = False
111
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000112 @source_util.writes_bytecode
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000113 def run_test(self, assertion):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000114 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000115 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
116 loader.load_module('_temp')
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000117 bytecode_path = source_util.bytecode_path(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000118 assertion(bytecode_path)
119
120 def test_bytecode_written(self):
121 fxn = lambda bc_path: self.assert_(os.path.exists(bc_path))
122 self.run_test(fxn)
123
124 def test_bytecode_not_written(self):
125 sys.dont_write_bytecode = True
126 fxn = lambda bc_path: self.assert_(not os.path.exists(bc_path))
127 self.run_test(fxn)
128
129
130class BadDataTest(unittest.TestCase):
131
132 """If the bytecode has a magic number that does not match the
133 interpreters', ImportError is raised [bad magic]. The timestamp can have
134 any value. And bad marshal data raises ValueError.
135
136 """
137
138 # [bad magic]
139 def test_bad_magic(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000140 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000141 py_compile.compile(mapping['_temp'])
142 os.unlink(mapping['_temp'])
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000143 bytecode_path = source_util.bytecode_path(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000144 with open(bytecode_path, 'r+b') as file:
145 file.seek(0)
146 file.write(b'\x00\x00\x00\x00')
147 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
148 self.assertRaises(ImportError, loader.load_module, '_temp')
149 self.assert_('_temp' not in sys.modules)
150
151
152class SourceBytecodeInteraction(unittest.TestCase):
153
154 """When both source and bytecode are present, certain rules dictate which
155 version of the code takes precedent. All things being equal, the bytecode
156 is used with the value of __file__ set to the source [basic top-level],
157 [basic package], [basic sub-module], [basic sub-package].
158
159 """
160
161 def import_(self, file, module, *, pkg=False):
162 loader = importlib._PyFileLoader(module, file, pkg)
163 return loader.load_module(module)
164
165 def run_test(self, test, *create, pkg=False):
166 create += (test,)
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000167 with source_util.create_modules(*create) as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000168 for name in create:
169 py_compile.compile(mapping[name])
170 if pkg:
171 import_name = test.rsplit('.', 1)[0]
172 else:
173 import_name = test
174 loader = importlib._PyFileLoader(import_name, mapping[test], pkg)
175 # Because some platforms only have a granularity to the second for
176 # atime you can't check the physical files. Instead just make it an
177 # exception trigger if source was read.
178 loader.get_source = lambda self, x: 42
179 module = loader.load_module(import_name)
180 self.assertEqual(module.__file__, mapping[name])
181 self.assert_(import_name in sys.modules)
182 self.assertEqual(id(module), id(sys.modules[import_name]))
183
184 # [basic top-level]
185 def test_basic_top_level(self):
186 self.run_test('top_level')
187
188 # [basic package]
189 def test_basic_package(self):
190 self.run_test('pkg.__init__', pkg=True)
191
192 # [basic sub-module]
193 def test_basic_sub_module(self):
194 self.run_test('pkg.sub', 'pkg.__init__')
195
196 # [basic sub-package]
197 def test_basic_sub_package(self):
198 self.run_test('pkg.sub.__init__', 'pkg.__init__', pkg=True)
199
200
201class BadBytecodeTest(unittest.TestCase):
202
203 """But there are several things about the bytecode which might lead to the
204 source being preferred. If the magic number differs from what the
205 interpreter uses, then the source is used with the bytecode regenerated.
206 If the timestamp is older than the modification time for the source then
207 the bytecode is not used [bad timestamp].
208
209 But if the marshal data is bad, even if the magic number and timestamp
210 work, a ValueError is raised and the source is not used [bad marshal].
211
212 """
213
214 def import_(self, file, module_name):
215 loader = importlib._PyFileLoader(module_name, file, False)
216 module = loader.load_module(module_name)
217 self.assert_(module_name in sys.modules)
218
219 # [bad magic]
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000220 @source_util.writes_bytecode
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000221 def test_bad_magic(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000222 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000223 py_compile.compile(mapping['_temp'])
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000224 bytecode_path = source_util.bytecode_path(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000225 with open(bytecode_path, 'r+b') as bytecode_file:
226 bytecode_file.seek(0)
227 bytecode_file.write(b'\x00\x00\x00\x00')
228 self.import_(mapping['_temp'], '_temp')
229 with open(bytecode_path, 'rb') as bytecode_file:
230 self.assertEqual(bytecode_file.read(4), imp.get_magic())
231
232 # [bad timestamp]
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000233 @source_util.writes_bytecode
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000234 def test_bad_bytecode(self):
235 zeros = b'\x00\x00\x00\x00'
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000236 with source_util.create_modules('_temp') as mapping:
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000237 py_compile.compile(mapping['_temp'])
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000238 bytecode_path = source_util.bytecode_path(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000239 with open(bytecode_path, 'r+b') as bytecode_file:
240 bytecode_file.seek(4)
241 bytecode_file.write(zeros)
242 self.import_(mapping['_temp'], '_temp')
243 source_mtime = os.path.getmtime(mapping['_temp'])
244 source_timestamp = importlib._w_long(source_mtime)
245 with open(bytecode_path, 'rb') as bytecode_file:
246 bytecode_file.seek(4)
247 self.assertEqual(bytecode_file.read(4), source_timestamp)
248
249 # [bad marshal]
250 def test_bad_marshal(self):
Brett Cannon4ee2cda2009-02-01 03:08:31 +0000251 with source_util.create_modules('_temp') as mapping:
252 bytecode_path = source_util.bytecode_path(mapping['_temp'])
Brett Cannon23cbd8a2009-01-18 00:24:28 +0000253 source_mtime = os.path.getmtime(mapping['_temp'])
254 source_timestamp = importlib._w_long(source_mtime)
255 with open(bytecode_path, 'wb') as bytecode_file:
256 bytecode_file.write(imp.get_magic())
257 bytecode_file.write(source_timestamp)
258 bytecode_file.write(b'AAAA')
259 self.assertRaises(ValueError, self.import_, mapping['_temp'],
260 '_temp')
261 self.assert_('_temp' not in sys.modules)
262
263
264def test_main():
265 from test.support import run_unittest
266 run_unittest(SimpleTest, DontWriteBytecodeTest, BadDataTest,
267 SourceBytecodeInteraction, BadBytecodeTest)
268
269
270if __name__ == '__main__':
271 test_main()