at master 4.0 kB view raw
1diff --git a/Lib/py_compile.py b/Lib/py_compile.py 2index 978da73d74..3559eb95ca 100644 3--- a/Lib/py_compile.py 4+++ b/Lib/py_compile.py 5@@ -120,16 +120,27 @@ def compile(file, cfile=None, dfile=None, doraise=False): 6 return 7 if cfile is None: 8 cfile = file + (__debug__ and 'c' or 'o') 9- with open(cfile, 'wb') as fc: 10- fc.write('\0\0\0\0') 11- if "DETERMINISTIC_BUILD" in os.environ: 12+ # Atomically write the pyc/pyo file. Issue #13146. 13+ # id() is used to generate a pseudo-random filename. 14+ path_tmp = '{}.{}'.format(cfile, id(cfile)) 15+ try: 16+ with open(path_tmp, 'wb') as fc: 17 fc.write('\0\0\0\0') 18- else: 19- wr_long(fc, timestamp) 20- marshal.dump(codeobject, fc) 21- fc.flush() 22- fc.seek(0, 0) 23- fc.write(MAGIC) 24+ if "DETERMINISTIC_BUILD" in os.environ: 25+ fc.write('\0\0\0\0') 26+ else: 27+ wr_long(fc, timestamp) 28+ marshal.dump(codeobject, fc) 29+ fc.flush() 30+ fc.seek(0, 0) 31+ fc.write(MAGIC) 32+ os.rename(path_tmp, cfile) 33+ except OSError: 34+ try: 35+ os.unlink(path_tmp) 36+ except OSError: 37+ pass 38+ raise 39 40 def main(args=None): 41 """Compile several source files. 42diff --git a/Python/import.c b/Python/import.c 43index 1e31d79279..f78a1efcf0 100644 44--- a/Python/import.c 45+++ b/Python/import.c 46@@ -951,6 +951,8 @@ static void 47 write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat, time_t mtime) 48 { 49 FILE *fp; 50+ size_t cpathname_len; 51+ char *cpathname_tmp; 52 #ifdef MS_WINDOWS /* since Windows uses different permissions */ 53 mode_t mode = srcstat->st_mode & ~S_IEXEC; 54 /* Issue #6074: We ensure user write access, so we can delete it later 55@@ -963,11 +965,28 @@ write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat, t 56 mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH; 57 #endif 58 59+#ifdef MS_WINDOWS 60 fp = open_exclusive(cpathname, mode); 61+#else 62+ /* Under POSIX, we first write to a tmp file and then take advantage 63+ of atomic renaming. */ 64+ cpathname_len = strlen(cpathname); 65+ cpathname_tmp = PyMem_MALLOC(cpathname_len + 5); 66+ if (cpathname_tmp == NULL) { 67+ PyErr_Clear(); 68+ return; 69+ } 70+ memcpy(cpathname_tmp, cpathname, cpathname_len); 71+ memcpy(cpathname_tmp + cpathname_len, ".tmp", 5); 72+ fp = open_exclusive(cpathname_tmp, mode); 73+#endif 74 if (fp == NULL) { 75 if (Py_VerboseFlag) 76 PySys_WriteStderr( 77 "# can't create %s\n", cpathname); 78+#ifndef MS_WINDOWS 79+ PyMem_FREE(cpathname_tmp); 80+#endif 81 return; 82 } 83 PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION); 84@@ -979,7 +998,12 @@ write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat, t 85 PySys_WriteStderr("# can't write %s\n", cpathname); 86 /* Don't keep partial file */ 87 fclose(fp); 88+#ifdef MS_WINDOWS 89 (void) unlink(cpathname); 90+#else 91+ (void) unlink(cpathname_tmp); 92+ PyMem_FREE(cpathname_tmp); 93+#endif 94 return; 95 } 96 /* Now write the true mtime (as a 32-bit field) */ 97@@ -989,6 +1013,19 @@ write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat, t 98 PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION); 99 fflush(fp); 100 } 101+ /* Under POSIX, do an atomic rename */ 102+#ifndef MS_WINDOWS 103+ if (rename(cpathname_tmp, cpathname)) { 104+ if (Py_VerboseFlag) 105+ PySys_WriteStderr("# can't write %s\n", cpathname); 106+ /* Don't keep tmp file */ 107+ fclose(fp); 108+ (void) unlink(cpathname_tmp); 109+ PyMem_FREE(cpathname_tmp); 110+ return; 111+ } 112+ PyMem_FREE(cpathname_tmp); 113+#endif 114 fclose(fp); 115 if (Py_VerboseFlag) 116 PySys_WriteStderr("# wrote %s\n", cpathname);