Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-126606: don't write incomplete pyc files #126627

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ 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)
brettcannon marked this conversation as resolved.
Show resolved Hide resolved
bytes_written = file.write(data)
if bytes_written != len(data):
raise OSError("os.write didn't write the full pyc file")
brettcannon marked this conversation as resolved.
Show resolved Hide resolved
_os.replace(path_tmp, path)
except OSError:
try:
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_importlib/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,5 +775,33 @@ 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):
from importlib import _bootstrap_external
from test.support import os_helper
import _pyio
import os
cfbolz marked this conversation as resolved.
Show resolved Hide resolved

oldwrite = os.write
seen_write = False

# emulate an os.write that only writes partial data
cfbolz marked this conversation as resolved.
Show resolved Hide resolved
def write(fd, data):
nonlocal seen_write
seen_write = True
return oldwrite(fd, data[:100])
brettcannon marked this conversation as resolved.
Show resolved Hide resolved

# need to patch _io to be _pyio, so that io.FileIO is affected by the
# os.write patch
cfbolz marked this conversation as resolved.
Show resolved Hide resolved
with (unittest.mock.patch('importlib._bootstrap_external._io', _pyio),
unittest.mock.patch('os.write', write)):
cfbolz marked this conversation as resolved.
Show resolved Hide resolved
with self.assertRaises(OSError):
_bootstrap_external._write_atomic(os_helper.TESTFN, b'x' * 10000)
assert seen_write

with self.assertRaises(OSError):
os.stat(os_helper.TESTFN) # did not get written
brettcannon marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix importlib to not write an incomplete pyc files when a ulimit or some
cfbolz marked this conversation as resolved.
Show resolved Hide resolved
other operating system mechanism is preventing the write to go through
fully.
Loading