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);