https://github.com/python/cpython/commit/df59b64589e32a2b2b43703cce62aad2ea0cfe1b
commit: df59b64589e32a2b2b43703cce62aad2ea0cfe1b
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: brettcannon <[email protected]>
date: 2024-11-13T14:49:47-08:00
summary:

[3.12] GH-126606: don't write incomplete pyc files (GH-126627) (GH-126810)

GH-126606: don't write incomplete pyc files (GH-126627)
(cherry picked from commit c695e37a3f95c225ee08d1e882d23fa200b5ec34)

Co-authored-by: CF Bolz-Tereick <[email protected]>
Co-authored-by: Kirill Podoprigora <[email protected]>
Co-authored-by: Brett Cannon <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst
M Lib/importlib/_bootstrap_external.py
M Lib/test/test_importlib/test_util.py

diff --git a/Lib/importlib/_bootstrap_external.py 
b/Lib/importlib/_bootstrap_external.py
index 61dafc0f4cb953..9b8a8dfc5aae28 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -204,7 +204,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:
diff --git a/Lib/test/test_importlib/test_util.py 
b/Lib/test/test_importlib/test_util.py
index e018af2e16beab..553e2087421835 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -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
@@ -758,5 +760,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()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst
new file mode 100644
index 00000000000000..9c0072304ded63
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-09-16-10-22.gh-issue-126066.9zs4m4.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to