| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -209,7 +209,11 @@ def _write_atomic(path, data, mode=0o666): | |||
| 209 | 209 | # We first write data to a temporary file, and then use os.replace() to | |
| 210 | 210 | # perform an atomic rename. | |
| 211 | 211 | with _io.FileIO(fd, 'wb') as file: | |
| 212 | - file.write(data) | ||
| 212 | + bytes_written = file.write(data) | ||
| 213 | + if bytes_written != len(data): | ||
| 214 | + # Raise an OSError so the 'except' below cleans up the partially | ||
| 215 | + # written file. | ||
| 216 | + raise OSError("os.write() didn't write the full pyc file") | ||
| 213 | 217 | _os.replace(path_tmp, path) | |
| 214 | 218 | except OSError: | |
| 215 | 219 | try: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,12 +6,14 @@ | |||
| 6 | 6 | importlib_util = util.import_importlib('importlib.util') | |
| 7 | 7 | ||
| 8 | 8 | import importlib.util | |
| 9 | + from importlib import _bootstrap_external | ||
| 9 | 10 | import os | |
| 10 | 11 | import pathlib | |
| 11 | 12 | import re | |
| 12 | 13 | import string | |
| 13 | 14 | import sys | |
| 14 | 15 | from test import support | |
| 16 | + from test.support import os_helper | ||
| 15 | 17 | import textwrap | |
| 16 | 18 | import types | |
| 17 | 19 | import unittest | |
@@ -775,5 +777,35 @@ def test_complete_multi_phase_init_module(self): | |||
| 775 | 777 | self.run_with_own_gil(script) | |
| 776 | 778 | ||
| 777 | 779 | ||
| 780 | + class MiscTests(unittest.TestCase): | ||
| 781 | + def test_atomic_write_should_notice_incomplete_writes(self): | ||
| 782 | + import _pyio | ||
| 783 | + | ||
| 784 | + oldwrite = os.write | ||
| 785 | + seen_write = False | ||
| 786 | + | ||
| 787 | + truncate_at_length = 100 | ||
| 788 | + | ||
| 789 | + # Emulate an os.write that only writes partial data. | ||
| 790 | + def write(fd, data): | ||
| 791 | + nonlocal seen_write | ||
| 792 | + seen_write = True | ||
| 793 | + return oldwrite(fd, data[:truncate_at_length]) | ||
| 794 | + | ||
| 795 | + # Need to patch _io to be _pyio, so that io.FileIO is affected by the | ||
| 796 | + # os.write patch. | ||
| 797 | + with (support.swap_attr(_bootstrap_external, '_io', _pyio), | ||
| 798 | + support.swap_attr(os, 'write', write)): | ||
| 799 | + with self.assertRaises(OSError): | ||
| 800 | + # Make sure we write something longer than the point where we | ||
| 801 | + # truncate. | ||
| 802 | + content = b'x' * (truncate_at_length * 2) | ||
| 803 | + _bootstrap_external._write_atomic(os_helper.TESTFN, content) | ||
| 804 | + assert seen_write | ||
| 805 | + | ||
| 806 | + with self.assertRaises(OSError): | ||
| 807 | + os.stat(support.os_helper.TESTFN) # Check that the file did not get written. | ||
| 808 | + | ||
| 809 | + | ||
| 778 | 810 | if __name__ == '__main__': | |
| 779 | 811 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Fix :mod:`importlib` to not write an incomplete .pyc files when a ulimit or some | ||
| 2 | + other operating system mechanism is preventing the write to go through | ||
| 3 | + fully. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments