Add platform.SaveFile

This commit is contained in:
Marian Beermann 2016-07-09 21:07:56 +02:00
parent f0930b89ce
commit 36ebc82748
3 changed files with 45 additions and 5 deletions

View File

@ -8,7 +8,7 @@ Public APIs are documented in platform.base.
from .base import acl_get, acl_set
from .base import set_flags, get_flags
from .base import SyncFile, sync_dir, fdatasync
from .base import SaveFile, SyncFile, sync_dir, fdatasync
from .base import swidth, API_VERSION
if sys.platform.startswith('linux'): # pragma: linux only

View File

@ -80,8 +80,11 @@ class SyncFile:
TODO: A Windows implementation should use CreateFile with FILE_FLAG_WRITE_THROUGH.
"""
def __init__(self, path):
self.fd = open(path, 'xb')
def __init__(self, path, binary=True):
mode = 'x'
if binary:
mode += 'b'
self.fd = open(path, mode)
self.fileno = self.fd.fileno()
def __enter__(self):
@ -112,6 +115,43 @@ class SyncFile:
platform.sync_dir(os.path.dirname(self.fd.name))
class SaveFile:
"""
Update file contents atomically.
Must be used as a context manager (defining the scope of the transaction).
On a journaling file system the file contents are always updated
atomically and won't become corrupted, even on pure failures or
crashes (for caveats see SyncFile).
"""
SUFFIX = '.tmp'
def __init__(self, path, binary=True):
self.binary = binary
self.path = path
self.tmppath = self.path + self.SUFFIX
def __enter__(self):
from .. import platform
try:
os.unlink(self.tmppath)
except OSError:
pass
self.fd = platform.SyncFile(self.tmppath, self.binary)
return self.fd
def __exit__(self, exc_type, exc_val, exc_tb):
from .. import platform
self.fd.close()
if exc_type is not None:
os.unlink(self.tmppath)
return
os.rename(self.tmppath, self.path)
platform.sync_dir(os.path.dirname(self.path))
def swidth(s):
"""terminal output width of string <s>

View File

@ -228,8 +228,8 @@ class SyncFile(BaseSyncFile):
disk in the immediate future.
"""
def __init__(self, path):
super().__init__(path)
def __init__(self, path, binary=True):
super().__init__(path, binary)
self.offset = 0
self.write_window = (16 * 1024 ** 2) & ~PAGE_MASK
self.last_sync = 0