]> kaliko git repositories - mpd-sima.git/blobdiff - sima/lib/httpcli/filelock.py
Persistent HTTP cache based on CacheControl
[mpd-sima.git] / sima / lib / httpcli / filelock.py
diff --git a/sima/lib/httpcli/filelock.py b/sima/lib/httpcli/filelock.py
new file mode 100644 (file)
index 0000000..6dc331b
--- /dev/null
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-\r
+# https://github.com/dmfrey/FileLock\r
+\r
+import os\r
+import time\r
+import errno\r
+\r
+class FileLockException(Exception):\r
+    pass\r
+\r
+class FileLock:\r
+    """ A file locking mechanism that has context-manager support so\r
+        you can use it in a with statement. This should be relatively cross\r
+        compatible as it doesn't rely on msvcrt or fcntl for the locking.\r
+    """\r
+\r
+    def __init__(self, file_name, timeout=10, delay=.05):\r
+        """ Prepare the file locker. Specify the file to lock and optionally\r
+            the maximum timeout and the delay between each attempt to lock.\r
+        """\r
+        self.is_locked = False\r
+        self.lockfile = os.path.join(os.getcwd(), "%s.lock" % file_name)\r
+        self.file_name = file_name\r
+        self.timeout = timeout\r
+        self.delay = delay\r
+\r
+\r
+    def acquire(self):\r
+        """ Acquire the lock, if possible. If the lock is in use, it check again\r
+            every `wait` seconds. It does this until it either gets the lock or\r
+            exceeds `timeout` number of seconds, in which case it throws\r
+            an exception.\r
+        """\r
+        start_time = time.time()\r
+        while True:\r
+            try:\r
+                self.fd = os.open(self.lockfile, os.O_CREAT|os.O_EXCL|os.O_RDWR)\r
+                break;\r
+            except OSError as e:\r
+                if e.errno != errno.EEXIST:\r
+                    raise\r
+                if (time.time() - start_time) >= self.timeout:\r
+                    raise FileLockException("Timeout occured.")\r
+                time.sleep(self.delay)\r
+        self.is_locked = True\r
+\r
+\r
+    def release(self):\r
+        """ Get rid of the lock by deleting the lockfile.\r
+            When working in a `with` statement, this gets automatically\r
+            called at the end.\r
+        """\r
+        if self.is_locked:\r
+            os.close(self.fd)\r
+            os.unlink(self.lockfile)\r
+            self.is_locked = False\r
+\r
+\r
+    def __enter__(self):\r
+        """ Activated when used in the with statement.\r
+            Should automatically acquire a lock to be used in the with block.\r
+        """\r
+        if not self.is_locked:\r
+            self.acquire()\r
+        return self\r
+\r
+\r
+    def __exit__(self, type, value, traceback):\r
+        """ Activated at the end of the with statement.\r
+            It automatically releases the lock if it isn't locked.\r
+        """\r
+        if self.is_locked:\r
+            self.release()\r
+\r
+\r
+    def __del__(self):\r
+        """ Make sure that the FileLock instance doesn't leave a lockfile\r
+            lying around.\r
+        """\r
+        self.release()\r