fileutil: write_atomic() for win32
authorMarko Kreen <markokr@gmail.com>
Mon, 18 Jun 2012 14:26:02 +0000 (17:26 +0300)
committerMarko Kreen <markokr@gmail.com>
Mon, 18 Jun 2012 14:26:02 +0000 (17:26 +0300)
win32 does not support atomic rename

Makefile
python/skytools/fileutil.py

index 911b926fe6c83342d955455274700e32880b9430..01fa7242ea79ba3e8ee9f208aadd50d2e2f2eb73 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ SUBDIRS = sql doc
 # modules that use doctest for regtests
 DOCTESTMODS = skytools.quoting skytools.parsing skytools.timeutil \
           skytools.sqltools skytools.querybuilder skytools.natsort \
-          skytools.utf8 skytools.sockutil
+          skytools.utf8 skytools.sockutil skytools.fileutil
 
 
 all: python-all sub-all config.mak
index c6fcefdd72db6726cfb7943ea6cacdb9bab71fd4..a88ce88d970b3829ee7bf40fb0ce919206a31492 100644 (file)
@@ -1,9 +1,22 @@
-"""File utilities"""
-
+"""File utilities
+
+>>> import tempfile, os
+>>> pidfn = tempfile.mktemp('.pid')
+>>> write_atomic(pidfn, "1")
+>>> write_atomic(pidfn, "2")
+>>> os.remove(pidfn)
+>>> write_atomic(pidfn, "1", '.bak')
+>>> write_atomic(pidfn, "2", '.bak')
+>>> os.remove(pidfn)
+"""
+
+import sys
 import os
+import errno
 
 __all__ = ['write_atomic', 'signal_pidfile']
 
+# non-win32
 def write_atomic(fn, data, bakext=None, mode='b'):
     """Write file with rename."""
 
@@ -32,6 +45,13 @@ def write_atomic(fn, data, bakext=None, mode='b'):
             if e.errno != errno.ENOENT:
                 raise
 
+    # win32 does not like replace
+    if sys.platform == 'win32':
+        try:
+            os.remove(fn)
+        except:
+            pass
+
     # atomically replace file
     os.rename(fn2, fn)
 
@@ -100,3 +120,46 @@ def win32_detect_pid(pid):
     k.CloseHandle(h)
     return code.value == STILL_ACTIVE
 
+def win32_write_atomic(fn, data, bakext=None, mode='b'):
+    """Write file with rename for win32."""
+
+    if mode not in ['', 'b', 't']:
+        raise ValueError("unsupported fopen mode")
+
+    # write new data to tmp file
+    fn2 = fn + '.new'
+    f = open(fn2, 'w' + mode)
+    f.write(data)
+    f.close()
+
+    # move old data to bak file
+    if bakext:
+        if bakext.find('/') >= 0:
+            raise ValueError("invalid bakext")
+        fnb = fn + bakext
+        try:
+            os.remove(fnb)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+        try:
+            os.rename(fn, fnb)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+    else:
+        try:
+            os.remove(fn)
+        except:
+            pass
+
+    # replace file
+    os.rename(fn2, fn)
+
+if sys.platform == 'win32':
+    write_atomic = win32_write_atomic
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+