mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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 |         # We first write data to a temporary file, and then use os.replace() to | ||||||
|         # perform an atomic rename. |         # perform an atomic rename. | ||||||
|         with _io.FileIO(fd, 'wb') as file: |         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) |         _os.replace(path_tmp, path) | ||||||
|     except OSError: |     except OSError: | ||||||
|         try: |         try: | ||||||
|  |  | ||||||
|  | @ -6,12 +6,14 @@ | ||||||
| importlib_util = util.import_importlib('importlib.util') | importlib_util = util.import_importlib('importlib.util') | ||||||
| 
 | 
 | ||||||
| import importlib.util | import importlib.util | ||||||
|  | from importlib import _bootstrap_external | ||||||
| import os | import os | ||||||
| import pathlib | import pathlib | ||||||
| import re | import re | ||||||
| import string | import string | ||||||
| import sys | import sys | ||||||
| from test import support | from test import support | ||||||
|  | from test.support import os_helper | ||||||
| import textwrap | import textwrap | ||||||
| import types | import types | ||||||
| import unittest | import unittest | ||||||
|  | @ -775,5 +777,35 @@ def test_complete_multi_phase_init_module(self): | ||||||
|             self.run_with_own_gil(script) |             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__': | if __name__ == '__main__': | ||||||
|     unittest.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