blob: c59dd2c174b88ad1aedd251e7129e515b2b3ad5d [file] [log] [blame]
Brett Cannon23cbd8a2009-01-18 00:24:28 +00001import importlib
Brett Cannon30b047d2009-02-01 02:05:11 +00002from .. import abc
Brett Cannon23cbd8a2009-01-18 00:24:28 +00003from .. import support
4
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 Cannon23cbd8a2009-01-18 00:24:28 +000021 with support.create_modules('_temp') as mapping:
22 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'],
26 '__package__': None}
27 for attr, value in check.items():
28 self.assertEqual(getattr(module, attr), value)
29
30 def test_package(self):
31 with support.create_modules('_pkg.__init__') as mapping:
32 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):
44 with support.create_modules('_pkg.__init__', '_pkg.mod')as mapping:
45 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):
59 with support.create_modules('_temp') as mapping:
60 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'
84 with support.create_modules(name) as mapping:
85 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):
97 with support.create_modules('_temp') as mapping:
98 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
112 @support.writes_bytecode
113 def run_test(self, assertion):
114 with support.create_modules('_temp') as mapping:
115 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
116 loader.load_module('_temp')
117 bytecode_path = support.bytecode_path(mapping['_temp'])
118 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):
140 with support.create_modules('_temp') as mapping:
141 py_compile.compile(mapping['_temp'])
142 os.unlink(mapping['_temp'])
143 bytecode_path = support.bytecode_path(mapping['_temp'])
144 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,)
167 with support.create_modules(*create) as mapping:
168 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]
220 @support.writes_bytecode
221 def test_bad_magic(self):
222 with support.create_modules('_temp') as mapping:
223 py_compile.compile(mapping['_temp'])
224 bytecode_path = support.bytecode_path(mapping['_temp'])
225 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]
233 @support.writes_bytecode
234 def test_bad_bytecode(self):
235 zeros = b'\x00\x00\x00\x00'
236 with support.create_modules('_temp') as mapping:
237 py_compile.compile(mapping['_temp'])
238 bytecode_path = support.bytecode_path(mapping['_temp'])
239 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):
251 with support.create_modules('_temp') as mapping:
252 bytecode_path = support.bytecode_path(mapping['_temp'])
253 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()