@@ -113,42 +113,116 @@ def test_bad_syntax(self):
113113
114114class BadBytecodeTest (unittest .TestCase ):
115115
116- """But there are several things about the bytecode which might lead to the
117- source being preferred. If the magic number differs from what the
118- interpreter uses, then the source is used with the bytecode regenerated.
119- If the timestamp is older than the modification time for the source then
120- the bytecode is not used [bad timestamp].
121-
122- But if the marshal data is bad, even if the magic number and timestamp
123- work, a ValueError is raised and the source is not used [bad marshal].
124-
125- The case of not being able to write out the bytecode must also be handled
126- as it's possible it was made read-only. In that instance the attempt to
127- write the bytecode should fail silently [bytecode read-only].
128-
129- """
130-
131116 def import_ (self , file , module_name ):
132117 loader = _bootstrap ._PyPycFileLoader (module_name , file , False )
133118 module = loader .load_module (module_name )
134119 self .assertTrue (module_name in sys .modules )
135120
136- # [bad magic]
121+ def manipulate_bytecode (self , name , mapping , manipulator , * ,
122+ del_source = False ):
123+ """Manipulate the bytecode of a module by passing it into a callable
124+ that returns what to use as the new bytecode."""
125+ try :
126+ del sys .modules ['_temp' ]
127+ except KeyError :
128+ pass
129+ py_compile .compile (mapping [name ])
130+ bytecode_path = source_util .bytecode_path (mapping [name ])
131+ with open (bytecode_path , 'rb' ) as file :
132+ bc = file .read ()
133+ new_bc = manipulator (bc )
134+ with open (bytecode_path , 'wb' ) as file :
135+ if new_bc :
136+ file .write (new_bc )
137+ if del_source :
138+ os .unlink (mapping [name ])
139+ return bytecode_path
140+
141+ @source_util .writes_bytecode_files
142+ def test_empty_file (self ):
143+ # When a .pyc is empty, regenerate it if possible, else raise
144+ # ImportError.
145+ with source_util .create_modules ('_temp' ) as mapping :
146+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
147+ lambda bc : None )
148+ self .import_ (mapping ['_temp' ], '_temp' )
149+ with open (bc_path , 'rb' ) as file :
150+ self .assertGreater (len (file .read ()), 8 )
151+ self .manipulate_bytecode ('_temp' , mapping , lambda bc : None ,
152+ del_source = True )
153+ with self .assertRaises (ImportError ):
154+ self .import_ (mapping ['_temp' ], '_temp' )
155+
156+ @source_util .writes_bytecode_files
157+ def test_partial_magic (self ):
158+ # When their are less than 4 bytes to a .pyc, regenerate it if
159+ # possible, else raise ImportError.
160+ with source_util .create_modules ('_temp' ) as mapping :
161+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
162+ lambda bc : bc [:3 ])
163+ self .import_ (mapping ['_temp' ], '_temp' )
164+ with open (bc_path , 'rb' ) as file :
165+ self .assertGreater (len (file .read ()), 8 )
166+ self .manipulate_bytecode ('_temp' , mapping , lambda bc : bc [:3 ],
167+ del_source = True )
168+ with self .assertRaises (ImportError ):
169+ self .import_ (mapping ['_temp' ], '_temp' )
170+
171+ @source_util .writes_bytecode_files
172+ def test_magic_only (self ):
173+ # When there is only the magic number, regenerate the .pyc if possible,
174+ # else raise EOFError.
175+ with source_util .create_modules ('_temp' ) as mapping :
176+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
177+ lambda bc : bc [:4 ])
178+ self .import_ (mapping ['_temp' ], '_temp' )
179+ with open (bc_path , 'rb' ) as file :
180+ self .assertGreater (len (file .read ()), 8 )
181+ self .manipulate_bytecode ('_temp' , mapping , lambda bc : bc [:4 ],
182+ del_source = True )
183+ with self .assertRaises (EOFError ):
184+ self .import_ (mapping ['_temp' ], '_temp' )
185+
186+ @source_util .writes_bytecode_files
187+ def test_partial_timestamp (self ):
188+ # When the timestamp is partial, regenerate the .pyc, else
189+ # raise EOFError.
190+ with source_util .create_modules ('_temp' ) as mapping :
191+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
192+ lambda bc : bc [:7 ])
193+ self .import_ (mapping ['_temp' ], '_temp' )
194+ with open (bc_path , 'rb' ) as file :
195+ self .assertGreater (len (file .read ()), 8 )
196+ self .manipulate_bytecode ('_temp' , mapping , lambda bc : bc [:7 ],
197+ del_source = True )
198+ with self .assertRaises (EOFError ):
199+ self .import_ (mapping ['_temp' ], '_temp' )
200+
201+ @source_util .writes_bytecode_files
202+ def test_no_marshal (self ):
203+ # When there is only the magic number and timestamp, raise EOFError.
204+ with source_util .create_modules ('_temp' ) as mapping :
205+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
206+ lambda bc : bc [:8 ])
207+ with self .assertRaises (EOFError ):
208+ self .import_ (mapping ['_temp' ], '_temp' )
209+
137210 @source_util .writes_bytecode_files
138211 def test_bad_magic (self ):
212+ # When the magic number is different, the bytecode should be
213+ # regenerated.
139214 with source_util .create_modules ('_temp' ) as mapping :
140- py_compile .compile (mapping ['_temp' ])
141- bytecode_path = source_util .bytecode_path (mapping ['_temp' ])
142- with open (bytecode_path , 'r+b' ) as bytecode_file :
143- bytecode_file .seek (0 )
144- bytecode_file .write (b'\x00 \x00 \x00 \x00 ' )
215+ bc_path = self .manipulate_bytecode ('_temp' , mapping ,
216+ lambda bc : b'\x00 \x00 \x00 \x00 ' + bc [4 :])
145217 self .import_ (mapping ['_temp' ], '_temp' )
146- with open (bytecode_path , 'rb' ) as bytecode_file :
218+ with open (bc_path , 'rb' ) as bytecode_file :
147219 self .assertEqual (bytecode_file .read (4 ), imp .get_magic ())
148220
149221 # [bad timestamp]
150222 @source_util .writes_bytecode_files
151223 def test_bad_bytecode (self ):
224+ # When the timestamp is older than the source, bytecode should be
225+ # regenerated.
152226 zeros = b'\x00 \x00 \x00 \x00 '
153227 with source_util .create_modules ('_temp' ) as mapping :
154228 py_compile .compile (mapping ['_temp' ])
@@ -166,6 +240,7 @@ def test_bad_bytecode(self):
166240 # [bad marshal]
167241 @source_util .writes_bytecode_files
168242 def test_bad_marshal (self ):
243+ # Bad marshal data should raise a ValueError.
169244 with source_util .create_modules ('_temp' ) as mapping :
170245 bytecode_path = source_util .bytecode_path (mapping ['_temp' ])
171246 source_mtime = os .path .getmtime (mapping ['_temp' ])
@@ -181,6 +256,7 @@ def test_bad_marshal(self):
181256 # [bytecode read-only]
182257 @source_util .writes_bytecode_files
183258 def test_read_only_bytecode (self ):
259+ # When bytecode is read-only but should be rewritten, fail silently.
184260 with source_util .create_modules ('_temp' ) as mapping :
185261 # Create bytecode that will need to be re-created.
186262 py_compile .compile (mapping ['_temp' ])
@@ -201,8 +277,7 @@ def test_read_only_bytecode(self):
201277
202278def test_main ():
203279 from test .support import run_unittest
204- run_unittest (SimpleTest , DontWriteBytecodeTest , BadDataTest ,
205- SourceBytecodeInteraction , BadBytecodeTest )
280+ run_unittest (SimpleTest , BadBytecodeTest )
206281
207282
208283if __name__ == '__main__' :
0 commit comments