mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #13146: Writing a pyc file is now atomic under POSIX.
This commit is contained in:
parent
5b9f4c1539
commit
707033a694
3 changed files with 63 additions and 14 deletions
|
@ -80,6 +80,27 @@ def _path_absolute(path):
|
||||||
return _path_join(_os.getcwd(), path)
|
return _path_join(_os.getcwd(), path)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_atomic(path, data):
|
||||||
|
"""Best-effort function to write data to a path atomically."""
|
||||||
|
if not sys.platform.startswith('win'):
|
||||||
|
# On POSIX-like platforms, renaming is atomic
|
||||||
|
path_tmp = path + '.tmp'
|
||||||
|
try:
|
||||||
|
fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY)
|
||||||
|
with _io.FileIO(fd, 'wb') as file:
|
||||||
|
file.write(data)
|
||||||
|
_os.rename(path_tmp, path)
|
||||||
|
except OSError:
|
||||||
|
try:
|
||||||
|
_os.unlink(path_tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
with _io.FileIO(path, 'wb') as file:
|
||||||
|
file.write(data)
|
||||||
|
|
||||||
|
|
||||||
def _wrap(new, old):
|
def _wrap(new, old):
|
||||||
"""Simple substitute for functools.wraps."""
|
"""Simple substitute for functools.wraps."""
|
||||||
for replace in ['__module__', '__name__', '__doc__']:
|
for replace in ['__module__', '__name__', '__doc__']:
|
||||||
|
@ -494,9 +515,8 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
try:
|
try:
|
||||||
with _io.FileIO(path, 'wb') as file:
|
_write_atomic(path, data)
|
||||||
file.write(data)
|
except OSError as exc:
|
||||||
except IOError as exc:
|
|
||||||
# Don't worry if you can't write bytecode.
|
# Don't worry if you can't write bytecode.
|
||||||
if exc.errno == errno.EACCES:
|
if exc.errno == errno.EACCES:
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #13146: Writing a pyc file is now atomic under POSIX.
|
||||||
|
|
||||||
- Issue #7833: Extension modules built using distutils on Windows will no
|
- Issue #7833: Extension modules built using distutils on Windows will no
|
||||||
longer include a "manifest" to prevent them failing at import time in some
|
longer include a "manifest" to prevent them failing at import time in some
|
||||||
embedded situations.
|
embedded situations.
|
||||||
|
|
|
@ -1284,7 +1284,8 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
int fd;
|
int fd;
|
||||||
#else
|
#else
|
||||||
PyObject *cpathbytes;
|
PyObject *cpathbytes, *cpathbytes_tmp;
|
||||||
|
Py_ssize_t cpathbytes_len;
|
||||||
#endif
|
#endif
|
||||||
PyObject *dirname;
|
PyObject *dirname;
|
||||||
Py_UCS4 *dirsep;
|
Py_UCS4 *dirsep;
|
||||||
|
@ -1345,13 +1346,25 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
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();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
cpathbytes_len = PyBytes_GET_SIZE(cpathbytes);
|
||||||
|
cpathbytes_tmp = PyBytes_FromStringAndSize(NULL, cpathbytes_len + 4);
|
||||||
|
if (cpathbytes_tmp == NULL) {
|
||||||
|
Py_DECREF(cpathbytes);
|
||||||
|
PyErr_Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(PyBytes_AS_STRING(cpathbytes_tmp), PyBytes_AS_STRING(cpathbytes),
|
||||||
|
cpathbytes_len);
|
||||||
|
memcpy(PyBytes_AS_STRING(cpathbytes_tmp) + cpathbytes_len, ".tmp", 4);
|
||||||
|
|
||||||
fp = open_exclusive(PyBytes_AS_STRING(cpathbytes), mode);
|
fp = open_exclusive(PyBytes_AS_STRING(cpathbytes_tmp), mode);
|
||||||
#endif
|
#endif
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
|
@ -1359,6 +1372,7 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
"# can't create %R\n", cpathname);
|
"# can't create %R\n", cpathname);
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
Py_DECREF(cpathbytes);
|
Py_DECREF(cpathbytes);
|
||||||
|
Py_DECREF(cpathbytes_tmp);
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1366,6 +1380,11 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
/* First write a 0 for mtime */
|
/* First write a 0 for mtime */
|
||||||
PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
|
PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
|
||||||
PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
|
PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
|
||||||
|
fflush(fp);
|
||||||
|
/* Now write the true mtime */
|
||||||
|
fseek(fp, 4L, 0);
|
||||||
|
assert(mtime < LONG_MAX);
|
||||||
|
PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION);
|
||||||
if (fflush(fp) != 0 || ferror(fp)) {
|
if (fflush(fp) != 0 || ferror(fp)) {
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
PySys_FormatStderr("# can't write %R\n", cpathname);
|
PySys_FormatStderr("# can't write %R\n", cpathname);
|
||||||
|
@ -1374,20 +1393,28 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname));
|
(void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname));
|
||||||
#else
|
#else
|
||||||
(void) unlink(PyBytes_AS_STRING(cpathbytes));
|
(void) unlink(PyBytes_AS_STRING(cpathbytes_tmp));
|
||||||
Py_DECREF(cpathbytes);
|
Py_DECREF(cpathbytes);
|
||||||
|
Py_DECREF(cpathbytes_tmp);
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifndef MS_WINDOWS
|
|
||||||
Py_DECREF(cpathbytes);
|
|
||||||
#endif
|
|
||||||
/* Now write the true mtime */
|
|
||||||
fseek(fp, 4L, 0);
|
|
||||||
assert(mtime < LONG_MAX);
|
|
||||||
PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION);
|
|
||||||
fflush(fp);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
/* Under POSIX, do an atomic rename */
|
||||||
|
#ifndef MS_WINDOWS
|
||||||
|
if (rename(PyBytes_AS_STRING(cpathbytes_tmp),
|
||||||
|
PyBytes_AS_STRING(cpathbytes))) {
|
||||||
|
if (Py_VerboseFlag)
|
||||||
|
PySys_FormatStderr("# can't write %R\n", cpathname);
|
||||||
|
/* Don't keep tmp file */
|
||||||
|
unlink(PyBytes_AS_STRING(cpathbytes_tmp));
|
||||||
|
Py_DECREF(cpathbytes);
|
||||||
|
Py_DECREF(cpathbytes_tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Py_DECREF(cpathbytes);
|
||||||
|
Py_DECREF(cpathbytes_tmp);
|
||||||
|
#endif
|
||||||
if (Py_VerboseFlag)
|
if (Py_VerboseFlag)
|
||||||
PySys_FormatStderr("# wrote %R\n", cpathname);
|
PySys_FormatStderr("# wrote %R\n", cpathname);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue