'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
--- /dev/null
+"""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)
+