Commit 24fef583 authored by Brett Cannon's avatar Brett Cannon

Have importlib use os.replace() for atomic renaming.

Closes issue #13961. Thanks to Charles-François Natali for the patch.
parent 7a67cb76
...@@ -137,26 +137,16 @@ def _path_absolute(path): ...@@ -137,26 +137,16 @@ def _path_absolute(path):
def _write_atomic(path, data): def _write_atomic(path, data):
"""Best-effort function to write data to a path atomically. """Function to write data to a path atomically."""
Be prepared to handle a FileExistsError if concurrent writing of the
temporary file is attempted."""
# Renaming should be atomic on most platforms (including Windows).
# Under Windows, the limitation is that we can't rename() to an existing
# path, while POSIX will overwrite it. But here we don't really care
# if there is a glimpse of time during which the final pyc file doesn't
# exist.
# id() is used to generate a pseudo-random filename. # id() is used to generate a pseudo-random filename.
path_tmp = '{}.{}'.format(path, id(path)) path_tmp = '{}.{}'.format(path, id(path))
fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY, 0o666) fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY, 0o666)
try: try:
# 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: with _io.FileIO(fd, 'wb') as file:
file.write(data) file.write(data)
try: _os.replace(path_tmp, path)
_os.rename(path_tmp, path)
except FileExistsError:
# Windows (if we had access to MoveFileEx, we could overwrite)
_os.unlink(path)
_os.rename(path_tmp, path)
except OSError: except OSError:
try: try:
_os.unlink(path_tmp) _os.unlink(path_tmp)
...@@ -602,9 +592,8 @@ class _SourceFileLoader(_FileLoader, SourceLoader): ...@@ -602,9 +592,8 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
return return
try: try:
_write_atomic(path, data) _write_atomic(path, data)
except (PermissionError, FileExistsError): except PermissionError:
# Don't worry if you can't write bytecode or someone is writing # Don't worry if you can't write bytecode.
# it at the same time.
pass pass
......
...@@ -466,6 +466,8 @@ Core and Builtins ...@@ -466,6 +466,8 @@ Core and Builtins
Library Library
------- -------
- Issue #13961: Move importlib over to using os.replace() for atomic renaming.
- Do away with ambiguous level values (as suggested by PEP 328) in - Do away with ambiguous level values (as suggested by PEP 328) in
importlib.__import__() by raising ValueError when level < 0. importlib.__import__() by raising ValueError when level < 0.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment