]> kaliko git repositories - mpd-sima.git/blob - sima/lib/httpcli/filelock.py
Persistent HTTP cache based on CacheControl
[mpd-sima.git] / sima / lib / httpcli / filelock.py
1 # -*- coding: utf-8 -*-\r
2 # https://github.com/dmfrey/FileLock\r
3 \r
4 import os\r
5 import time\r
6 import errno\r
7 \r
8 class FileLockException(Exception):\r
9     pass\r
10 \r
11 class FileLock:\r
12     """ A file locking mechanism that has context-manager support so\r
13         you can use it in a with statement. This should be relatively cross\r
14         compatible as it doesn't rely on msvcrt or fcntl for the locking.\r
15     """\r
16 \r
17     def __init__(self, file_name, timeout=10, delay=.05):\r
18         """ Prepare the file locker. Specify the file to lock and optionally\r
19             the maximum timeout and the delay between each attempt to lock.\r
20         """\r
21         self.is_locked = False\r
22         self.lockfile = os.path.join(os.getcwd(), "%s.lock" % file_name)\r
23         self.file_name = file_name\r
24         self.timeout = timeout\r
25         self.delay = delay\r
26 \r
27 \r
28     def acquire(self):\r
29         """ Acquire the lock, if possible. If the lock is in use, it check again\r
30             every `wait` seconds. It does this until it either gets the lock or\r
31             exceeds `timeout` number of seconds, in which case it throws\r
32             an exception.\r
33         """\r
34         start_time = time.time()\r
35         while True:\r
36             try:\r
37                 self.fd = os.open(self.lockfile, os.O_CREAT|os.O_EXCL|os.O_RDWR)\r
38                 break;\r
39             except OSError as e:\r
40                 if e.errno != errno.EEXIST:\r
41                     raise\r
42                 if (time.time() - start_time) >= self.timeout:\r
43                     raise FileLockException("Timeout occured.")\r
44                 time.sleep(self.delay)\r
45         self.is_locked = True\r
46 \r
47 \r
48     def release(self):\r
49         """ Get rid of the lock by deleting the lockfile.\r
50             When working in a `with` statement, this gets automatically\r
51             called at the end.\r
52         """\r
53         if self.is_locked:\r
54             os.close(self.fd)\r
55             os.unlink(self.lockfile)\r
56             self.is_locked = False\r
57 \r
58 \r
59     def __enter__(self):\r
60         """ Activated when used in the with statement.\r
61             Should automatically acquire a lock to be used in the with block.\r
62         """\r
63         if not self.is_locked:\r
64             self.acquire()\r
65         return self\r
66 \r
67 \r
68     def __exit__(self, type, value, traceback):\r
69         """ Activated at the end of the with statement.\r
70             It automatically releases the lock if it isn't locked.\r
71         """\r
72         if self.is_locked:\r
73             self.release()\r
74 \r
75 \r
76     def __del__(self):\r
77         """ Make sure that the FileLock instance doesn't leave a lockfile\r
78             lying around.\r
79         """\r
80         self.release()\r