##// END OF EJS Templates
Copy file metadata in atomic save...
Thomas Kluyver -
Show More
@@ -19,6 +19,8 b' import codecs'
19 19 from contextlib import contextmanager
20 20 import io
21 21 import os
22 import shutil
23 import stat
22 24 import sys
23 25 import tempfile
24 26 from .capture import CapturedIO, capture_output
@@ -219,6 +221,17 b" def temp_pyfile(src, ext='.py'):"
219 221 f.flush()
220 222 return fname, f
221 223
224 def _copy_metadata(src, dst):
225 """Copy the set of metadata we want for atomic_writing.
226
227 Permission bits and flags. We'd like to copy file ownership as well, but we
228 can't do that.
229 """
230 shutil.copymode(src, dst)
231 st = os.stat(src)
232 if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
233 os.chflags(st.st_flags)
234
222 235 @contextmanager
223 236 def atomic_writing(path, text=True, encoding='utf-8', **kwargs):
224 237 """Context manager to write to a file only if the entire write is successful.
@@ -268,6 +281,13 b" def atomic_writing(path, text=True, encoding='utf-8', **kwargs):"
268 281 # Written successfully, now rename it
269 282 fileobj.close()
270 283
284 # Copy permission bits, access time, etc.
285 try:
286 _copy_metadata(path, tmp_path)
287 except OSError:
288 # e.g. the file didn't already exist. Ignore any failure to copy metadata
289 pass
290
271 291 if os.name == 'nt' and os.path.exists(path):
272 292 # Rename over existing file doesn't work on Windows
273 293 os.remove(path)
@@ -16,6 +16,7 b' from __future__ import absolute_import'
16 16
17 17 import io as stdlib_io
18 18 import os.path
19 import stat
19 20 import sys
20 21
21 22 from subprocess import Popen, PIPE
@@ -134,6 +135,10 b' def test_atomic_writing():'
134 135 f1 = os.path.join(td, 'penguin')
135 136 with stdlib_io.open(f1, 'w') as f:
136 137 f.write(u'Before')
138
139 if os.name != 'nt':
140 os.chmod(f1, 0o701)
141 orig_mode = stat.S_IMODE(os.stat(f1).st_mode)
137 142
138 143 with nt.assert_raises(CustomExc):
139 144 with atomic_writing(f1) as f:
@@ -149,3 +154,7 b' def test_atomic_writing():'
149 154
150 155 with stdlib_io.open(f1, 'r') as f:
151 156 nt.assert_equal(f.read(), u'Overwritten')
157
158 if os.name != 'nt':
159 mode = stat.S_IMODE(os.stat(f1).st_mode)
160 nt.assert_equal(mode, orig_mode)
General Comments 0
You need to be logged in to leave comments. Login now