skytools.fileutil: new module, contains write_atomic()
authorMarko Kreen <markokr@gmail.com>
Fri, 15 Jun 2012 13:32:06 +0000 (16:32 +0300)
committerMarko Kreen <markokr@gmail.com>
Fri, 15 Jun 2012 13:32:06 +0000 (16:32 +0300)
python/skytools/__init__.py
python/skytools/fileutil.py [new file with mode: 0644]

index 6bdeb51e1c87679f705634bc14cad157c5253aff..4f6d1bdef9068b8d976473e28a208c437899574b 100644 (file)
@@ -29,6 +29,8 @@ _symbols = {
     'T_SEQUENCE': 'skytools.dbstruct:T_SEQUENCE',
     'T_TABLE': 'skytools.dbstruct:T_TABLE',
     'T_TRIGGER': 'skytools.dbstruct:T_TRIGGER',
+    # skytools.fileutil
+    'write_atomic': 'skytools.fileutil:write_atomic',
     # skytools.gzlog
     'gzip_append': 'skytools.gzlog:gzip_append',
     # skytools.parsing
diff --git a/python/skytools/fileutil.py b/python/skytools/fileutil.py
new file mode 100644 (file)
index 0000000..fd657fd
--- /dev/null
@@ -0,0 +1,37 @@
+"""File utilities"""
+
+import os
+
+__all__ = ['write_atomic']
+
+def write_atomic(fn, data, bakext=None, mode='b'):
+    """Write file with rename."""
+
+    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()
+
+    # link old data to bak file
+    if bakext:
+        if bakext.find('/') >= 0:
+            raise ValueError("invalid bakext")
+        fnb = fn + bakext
+        try:
+            os.unlink(fnb)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+        try:
+            os.link(fn, fnb)
+        except OSError, e:
+            if e.errno != errno.ENOENT:
+                raise
+
+    # atomically replace file
+    os.rename(fn2, fn)
+