blob: 249bdc0c7c1f0e02a9fad1efa1f44580ec06e188 [file] [log] [blame]
Brett Cannon23cbd8a2009-01-18 00:24:28 +00001import importlib
2from .. import support
3
4import imp
5import os
6import py_compile
7import sys
8import unittest
9
10
11class SimpleTest(unittest.TestCase):
12
13 """Should have no issue importing a source module [basic]. And if there is
14 a syntax error, it should raise a SyntaxError [syntax error].
15
16 """
17
18 # [basic]
19 def test_basic(self):
20 with support.create_modules('_temp') as mapping:
21 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
22 loader.load_module('_temp')
23 self.assert_('_temp' in sys.modules)
24
25 # [syntax error]
26 def test_bad_syntax(self):
27 with support.create_modules('_temp') as mapping:
28 with open(mapping['_temp'], 'w') as file:
29 file.write('=')
30 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
31 self.assertRaises(SyntaxError, loader.load_module, '_temp')
32 self.assert_('_temp' not in sys.modules)
33
34
35class DontWriteBytecodeTest(unittest.TestCase):
36
37 """If sys.dont_write_bytcode is true then no bytecode should be created."""
38
39 def tearDown(self):
40 sys.dont_write_bytecode = False
41
42 @support.writes_bytecode
43 def run_test(self, assertion):
44 with support.create_modules('_temp') as mapping:
45 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
46 loader.load_module('_temp')
47 bytecode_path = support.bytecode_path(mapping['_temp'])
48 assertion(bytecode_path)
49
50 def test_bytecode_written(self):
51 fxn = lambda bc_path: self.assert_(os.path.exists(bc_path))
52 self.run_test(fxn)
53
54 def test_bytecode_not_written(self):
55 sys.dont_write_bytecode = True
56 fxn = lambda bc_path: self.assert_(not os.path.exists(bc_path))
57 self.run_test(fxn)
58
59
60class BadDataTest(unittest.TestCase):
61
62 """If the bytecode has a magic number that does not match the
63 interpreters', ImportError is raised [bad magic]. The timestamp can have
64 any value. And bad marshal data raises ValueError.
65
66 """
67
68 # [bad magic]
69 def test_bad_magic(self):
70 with support.create_modules('_temp') as mapping:
71 py_compile.compile(mapping['_temp'])
72 os.unlink(mapping['_temp'])
73 bytecode_path = support.bytecode_path(mapping['_temp'])
74 with open(bytecode_path, 'r+b') as file:
75 file.seek(0)
76 file.write(b'\x00\x00\x00\x00')
77 loader = importlib._PyFileLoader('_temp', mapping['_temp'], False)
78 self.assertRaises(ImportError, loader.load_module, '_temp')
79 self.assert_('_temp' not in sys.modules)
80
81
82class SourceBytecodeInteraction(unittest.TestCase):
83
84 """When both source and bytecode are present, certain rules dictate which
85 version of the code takes precedent. All things being equal, the bytecode
86 is used with the value of __file__ set to the source [basic top-level],
87 [basic package], [basic sub-module], [basic sub-package].
88
89 """
90
91 def import_(self, file, module, *, pkg=False):
92 loader = importlib._PyFileLoader(module, file, pkg)
93 return loader.load_module(module)
94
95 def run_test(self, test, *create, pkg=False):
96 create += (test,)
97 with support.create_modules(*create) as mapping:
98 for name in create:
99 py_compile.compile(mapping[name])
100 if pkg:
101 import_name = test.rsplit('.', 1)[0]
102 else:
103 import_name = test
104 loader = importlib._PyFileLoader(import_name, mapping[test], pkg)
105 # Because some platforms only have a granularity to the second for
106 # atime you can't check the physical files. Instead just make it an
107 # exception trigger if source was read.
108 loader.get_source = lambda self, x: 42
109 module = loader.load_module(import_name)
110 self.assertEqual(module.__file__, mapping[name])
111 self.assert_(import_name in sys.modules)
112 self.assertEqual(id(module), id(sys.modules[import_name]))
113
114 # [basic top-level]
115 def test_basic_top_level(self):
116 self.run_test('top_level')
117
118 # [basic package]
119 def test_basic_package(self):
120 self.run_test('pkg.__init__', pkg=True)
121
122 # [basic sub-module]
123 def test_basic_sub_module(self):
124 self.run_test('pkg.sub', 'pkg.__init__')
125
126 # [basic sub-package]
127 def test_basic_sub_package(self):
128 self.run_test('pkg.sub.__init__', 'pkg.__init__', pkg=True)
129
130
131class BadBytecodeTest(unittest.TestCase):
132
133 """But there are several things about the bytecode which might lead to the
134 source being preferred. If the magic number differs from what the
135 interpreter uses, then the source is used with the bytecode regenerated.
136 If the timestamp is older than the modification time for the source then
137 the bytecode is not used [bad timestamp].
138
139 But if the marshal data is bad, even if the magic number and timestamp
140 work, a ValueError is raised and the source is not used [bad marshal].
141
142 """
143
144 def import_(self, file, module_name):
145 loader = importlib._PyFileLoader(module_name, file, False)
146 module = loader.load_module(module_name)
147 self.assert_(module_name in sys.modules)
148
149 # [bad magic]
150 @support.writes_bytecode
151 def test_bad_magic(self):
152 with support.create_modules('_temp') as mapping:
153 py_compile.compile(mapping['_temp'])
154 bytecode_path = support.bytecode_path(mapping['_temp'])
155 with open(bytecode_path, 'r+b') as bytecode_file:
156 bytecode_file.seek(0)
157 bytecode_file.write(b'\x00\x00\x00\x00')
158 self.import_(mapping['_temp'], '_temp')
159 with open(bytecode_path, 'rb') as bytecode_file:
160 self.assertEqual(bytecode_file.read(4), imp.get_magic())
161
162 # [bad timestamp]
163 @support.writes_bytecode
164 def test_bad_bytecode(self):
165 zeros = b'\x00\x00\x00\x00'
166 with support.create_modules('_temp') as mapping:
167 py_compile.compile(mapping['_temp'])
168 bytecode_path = support.bytecode_path(mapping['_temp'])
169 with open(bytecode_path, 'r+b') as bytecode_file:
170 bytecode_file.seek(4)
171 bytecode_file.write(zeros)
172 self.import_(mapping['_temp'], '_temp')
173 source_mtime = os.path.getmtime(mapping['_temp'])
174 source_timestamp = importlib._w_long(source_mtime)
175 with open(bytecode_path, 'rb') as bytecode_file:
176 bytecode_file.seek(4)
177 self.assertEqual(bytecode_file.read(4), source_timestamp)
178
179 # [bad marshal]
180 def test_bad_marshal(self):
181 with support.create_modules('_temp') as mapping:
182 bytecode_path = support.bytecode_path(mapping['_temp'])
183 source_mtime = os.path.getmtime(mapping['_temp'])
184 source_timestamp = importlib._w_long(source_mtime)
185 with open(bytecode_path, 'wb') as bytecode_file:
186 bytecode_file.write(imp.get_magic())
187 bytecode_file.write(source_timestamp)
188 bytecode_file.write(b'AAAA')
189 self.assertRaises(ValueError, self.import_, mapping['_temp'],
190 '_temp')
191 self.assert_('_temp' not in sys.modules)
192
193
194def test_main():
195 from test.support import run_unittest
196 run_unittest(SimpleTest, DontWriteBytecodeTest, BadDataTest,
197 SourceBytecodeInteraction, BadBytecodeTest)
198
199
200if __name__ == '__main__':
201 test_main()