diff lib/python/lock.py @ 87:b6a5999d8e06

working with locking and copying
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Tue, 16 Mar 2021 16:20:02 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/python/lock.py	Tue Mar 16 16:20:02 2021 +0000
@@ -0,0 +1,34 @@
+# Courtesy of https://stackoverflow.com/a/46407326
+import fcntl, os
+def lock_file(f):
+  if f.writable(): fcntl.lockf(f, fcntl.LOCK_EX)
+def unlock_file(f):
+  if f.writable(): fcntl.lockf(f, fcntl.LOCK_UN)
+
+# Class for ensuring that all file operations are atomic, treat
+# initialization like a standard call to 'open' that happens to be atomic.
+# This file opener *must* be used in a "with" block.
+class AtomicOpen:
+    # Open the file with arguments provided by user. Then acquire
+    # a lock on that file object (WARNING: Advisory locking).
+    def __init__(self, path, *args, **kwargs):
+        # Open the file and acquire a lock on the file before operating
+        self.file = open(path,*args, **kwargs)
+        # Lock the opened file
+        lock_file(self.file)
+
+    # Return the opened file object (knowing a lock has been obtained).
+    def __enter__(self, *args, **kwargs): return self.file
+
+    # Unlock the file and close the file object.
+    def __exit__(self, exc_type=None, exc_value=None, traceback=None):        
+        # Flush to make sure all buffered contents are written to file.
+        self.file.flush()
+        os.fsync(self.file.fileno())
+        # Release the lock on the file.
+        unlock_file(self.file)
+        self.file.close()
+        # Handle exceptions that may have come up during execution, by
+        # default any exceptions are raised to the user.
+        if (exc_type != None): return False
+        else:                  return True