# 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
-"""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."""
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)
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()
+