mirror of https://github.com/python/cpython
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
|
@ -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 @@ machinery = util.import_importlib('importlib.machinery')
|
|||
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 @@ class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):
|
|||
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…
Reference in New Issue