mirror of https://github.com/python/cpython.git
Issue #13392: Writing a pyc file should now be atomic under Windows as well.
This commit is contained in:
parent
5f7f6150c3
commit
28e401e717
|
@ -84,14 +84,22 @@ def _write_atomic(path, data):
|
||||||
"""Best-effort function to write data to a path atomically.
|
"""Best-effort function to write data to a path atomically.
|
||||||
Be prepared to handle a FileExistsError if concurrent writing of the
|
Be prepared to handle a FileExistsError if concurrent writing of the
|
||||||
temporary file is attempted."""
|
temporary file is attempted."""
|
||||||
if not sys.platform.startswith('win'):
|
# Renaming should be atomic on most platforms (including Windows).
|
||||||
# On POSIX-like platforms, renaming is atomic. id() is used to generate
|
# Under Windows, the limitation is that we can't rename() to an existing
|
||||||
# a pseudo-random filename.
|
# 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.
|
||||||
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:
|
||||||
with _io.FileIO(fd, 'wb') as file:
|
with _io.FileIO(fd, 'wb') as file:
|
||||||
file.write(data)
|
file.write(data)
|
||||||
|
try:
|
||||||
|
_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)
|
_os.rename(path_tmp, path)
|
||||||
except OSError:
|
except OSError:
|
||||||
try:
|
try:
|
||||||
|
@ -99,9 +107,6 @@ def _write_atomic(path, data):
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
raise
|
raise
|
||||||
else:
|
|
||||||
with _io.FileIO(path, 'wb') as file:
|
|
||||||
file.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap(new, old):
|
def _wrap(new, old):
|
||||||
|
|
|
@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #13392: Writing a pyc file should now be atomic under Windows as well.
|
||||||
|
|
||||||
- Issue #13333: The UTF-7 decoder now accepts lone surrogates (the encoder
|
- Issue #13333: The UTF-7 decoder now accepts lone surrogates (the encoder
|
||||||
already accepts them).
|
already accepts them).
|
||||||
|
|
||||||
|
|
|
@ -1197,6 +1197,8 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
time_t mtime = srcstat->st_mtime;
|
time_t mtime = srcstat->st_mtime;
|
||||||
#ifdef MS_WINDOWS /* since Windows uses different permissions */
|
#ifdef MS_WINDOWS /* since Windows uses different permissions */
|
||||||
mode_t mode = srcstat->st_mode & ~S_IEXEC;
|
mode_t mode = srcstat->st_mode & ~S_IEXEC;
|
||||||
|
PyObject *cpathname_tmp;
|
||||||
|
Py_ssize_t cpathname_len;
|
||||||
#else
|
#else
|
||||||
mode_t dirmode = (srcstat->st_mode |
|
mode_t dirmode = (srcstat->st_mode |
|
||||||
S_IXUSR | S_IXGRP | S_IXOTH |
|
S_IXUSR | S_IXGRP | S_IXOTH |
|
||||||
|
@ -1255,18 +1257,29 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
}
|
}
|
||||||
Py_DECREF(dirname);
|
Py_DECREF(dirname);
|
||||||
|
|
||||||
|
/* We first write to a tmp file and then take advantage
|
||||||
|
of atomic renaming (which *should* be true even under Windows). */
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname));
|
cpathname_len = PyUnicode_GET_LENGTH(cpathname);
|
||||||
fd = _wopen(PyUnicode_AS_UNICODE(cpathname),
|
cpathname_tmp = PyUnicode_New(cpathname_len + 4,
|
||||||
O_EXCL | O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
|
PyUnicode_MAX_CHAR_VALUE(cpathname));
|
||||||
|
if (cpathname_tmp == NULL) {
|
||||||
|
PyErr_Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PyUnicode_WriteChar(cpathname_tmp, cpathname_len + 0, '.');
|
||||||
|
PyUnicode_WriteChar(cpathname_tmp, cpathname_len + 1, 't');
|
||||||
|
PyUnicode_WriteChar(cpathname_tmp, cpathname_len + 2, 'm');
|
||||||
|
PyUnicode_WriteChar(cpathname_tmp, cpathname_len + 3, 'p');
|
||||||
|
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname_tmp));
|
||||||
|
fd = _wopen(PyUnicode_AS_UNICODE(cpathname_tmp),
|
||||||
|
O_EXCL | O_CREAT | O_WRONLY | O_BINARY,
|
||||||
mode);
|
mode);
|
||||||
if (0 <= fd)
|
if (0 <= fd)
|
||||||
fp = fdopen(fd, "wb");
|
fp = fdopen(fd, "wb");
|
||||||
else
|
else
|
||||||
fp = NULL;
|
fp = NULL;
|
||||||
#else
|
#else
|
||||||
/* Under POSIX, we first write to a tmp file and then take advantage
|
|
||||||
of atomic renaming. */
|
|
||||||
cpathbytes = PyUnicode_EncodeFSDefault(cpathname);
|
cpathbytes = PyUnicode_EncodeFSDefault(cpathname);
|
||||||
if (cpathbytes == NULL) {
|
if (cpathbytes == NULL) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
|
@ -1294,7 +1307,9 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
PySys_FormatStderr(
|
PySys_FormatStderr(
|
||||||
"# can't create %R\n", cpathname);
|
"# can't create %R\n", cpathname);
|
||||||
#ifndef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
Py_DECREF(cpathname_tmp);
|
||||||
|
#else
|
||||||
Py_DECREF(cpathbytes);
|
Py_DECREF(cpathbytes);
|
||||||
Py_DECREF(cpathbytes_tmp);
|
Py_DECREF(cpathbytes_tmp);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1315,7 +1330,8 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
/* Don't keep partial file */
|
/* Don't keep partial file */
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname));
|
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname_tmp));
|
||||||
|
Py_DECREF(cpathname_tmp);
|
||||||
#else
|
#else
|
||||||
(void) unlink(PyBytes_AS_STRING(cpathbytes_tmp));
|
(void) unlink(PyBytes_AS_STRING(cpathbytes_tmp));
|
||||||
Py_DECREF(cpathbytes);
|
Py_DECREF(cpathbytes);
|
||||||
|
@ -1324,8 +1340,20 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
/* Under POSIX, do an atomic rename */
|
/* Do a (hopefully) atomic rename */
|
||||||
#ifndef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
if (!MoveFileExW(PyUnicode_AS_UNICODE(cpathname_tmp),
|
||||||
|
PyUnicode_AS_UNICODE(cpathname),
|
||||||
|
MOVEFILE_REPLACE_EXISTING)) {
|
||||||
|
if (Py_VerboseFlag)
|
||||||
|
PySys_FormatStderr("# can't write %R\n", cpathname);
|
||||||
|
/* Don't keep tmp file */
|
||||||
|
(void) DeleteFileW(PyUnicode_AS_UNICODE(cpathname_tmp));
|
||||||
|
Py_DECREF(cpathname_tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Py_DECREF(cpathname_tmp);
|
||||||
|
#else
|
||||||
if (rename(PyBytes_AS_STRING(cpathbytes_tmp),
|
if (rename(PyBytes_AS_STRING(cpathbytes_tmp),
|
||||||
PyBytes_AS_STRING(cpathbytes))) {
|
PyBytes_AS_STRING(cpathbytes))) {
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
|
|
Loading…
Reference in New Issue