mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	GH-126606: don't write incomplete pyc files (GH-126627)
Co-authored-by: Kirill Podoprigora <kirill.bast9@mail.ru> Co-authored-by: Brett Cannon <brett@python.org>
This commit is contained in:
		
							parent
							
								
									f6b0361c17
								
							
						
					
					
						commit
						c695e37a3f
					
				
					 3 changed files with 40 additions and 1 deletions
				
			
		|  | @ -209,7 +209,11 @@ def _write_atomic(path, data, mode=0o666): | |||
|         # We first write data to a temporary file, and then use os.replace() to | ||||
|         # perform an atomic rename. | ||||
|         with _io.FileIO(fd, 'wb') as file: | ||||
|             file.write(data) | ||||
|             bytes_written = file.write(data) | ||||
|         if bytes_written != len(data): | ||||
|             # Raise an OSError so the 'except' below cleans up the partially | ||||
|             # written file. | ||||
|             raise OSError("os.write() didn't write the full pyc file") | ||||
|         _os.replace(path_tmp, path) | ||||
|     except OSError: | ||||
|         try: | ||||
|  |  | |||
|  | @ -6,12 +6,14 @@ | |||
| importlib_util = util.import_importlib('importlib.util') | ||||
| 
 | ||||
| import importlib.util | ||||
| from importlib import _bootstrap_external | ||||
| import os | ||||
| import pathlib | ||||
| import re | ||||
| import string | ||||
| import sys | ||||
| from test import support | ||||
| from test.support import os_helper | ||||
| import textwrap | ||||
| import types | ||||
| import unittest | ||||
|  | @ -775,5 +777,35 @@ def test_complete_multi_phase_init_module(self): | |||
|             self.run_with_own_gil(script) | ||||
| 
 | ||||
| 
 | ||||
| class MiscTests(unittest.TestCase): | ||||
|     def test_atomic_write_should_notice_incomplete_writes(self): | ||||
|         import _pyio | ||||
| 
 | ||||
|         oldwrite = os.write | ||||
|         seen_write = False | ||||
| 
 | ||||
|         truncate_at_length = 100 | ||||
| 
 | ||||
|         # Emulate an os.write that only writes partial data. | ||||
|         def write(fd, data): | ||||
|             nonlocal seen_write | ||||
|             seen_write = True | ||||
|             return oldwrite(fd, data[:truncate_at_length]) | ||||
| 
 | ||||
|         # Need to patch _io to be _pyio, so that io.FileIO is affected by the | ||||
|         # os.write patch. | ||||
|         with (support.swap_attr(_bootstrap_external, '_io', _pyio), | ||||
|               support.swap_attr(os, 'write', write)): | ||||
|             with self.assertRaises(OSError): | ||||
|                 # Make sure we write something longer than the point where we | ||||
|                 # truncate. | ||||
|                 content = b'x' * (truncate_at_length * 2) | ||||
|                 _bootstrap_external._write_atomic(os_helper.TESTFN, content) | ||||
|         assert seen_write | ||||
| 
 | ||||
|         with self.assertRaises(OSError): | ||||
|             os.stat(support.os_helper.TESTFN) # Check that the file did not get written. | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| Fix :mod:`importlib` to not write an incomplete .pyc files when a ulimit or some | ||||
| other operating system mechanism is preventing the write to go through | ||||
| fully. | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 CF Bolz-Tereick
						CF Bolz-Tereick