Commit | Line | Data |
---|---|---|
c638d827 CR |
1 | # portalocker.py - Cross-platform (posix/nt) API for flock-style file locking. |
2 | # Requires python 1.5.2 or better. | |
3 | ||
4 | # ID line added by richard for Roundup file tracking | |
5 | # $Id: portalocker.py,v 1.9 2006-09-09 05:42:45 richard Exp $ | |
6 | ||
7 | """Cross-platform (posix/nt) API for flock-style file locking. | |
8 | ||
9 | Synopsis:: | |
10 | ||
11 | import portalocker | |
12 | file = open("somefile", "r+") | |
13 | portalocker.lock(file, portalocker.LOCK_EX) | |
14 | file.seek(12) | |
15 | file.write("foo") | |
16 | file.close() | |
17 | ||
18 | If you know what you're doing, you may choose to:: | |
19 | ||
20 | portalocker.unlock(file) | |
21 | ||
22 | before closing the file, but why? | |
23 | ||
24 | Methods:: | |
25 | ||
26 | lock( file, flags ) | |
27 | unlock( file ) | |
28 | ||
29 | Constants:: | |
30 | ||
31 | LOCK_EX | |
32 | LOCK_SH | |
33 | LOCK_NB | |
34 | ||
35 | I learned the win32 technique for locking files from sample code | |
36 | provided by John Nielsen <nielsenjf@my-deja.com> in the documentation | |
37 | that accompanies the win32 modules. | |
38 | ||
39 | :Author: Jonathan Feinberg <jdf@pobox.com> | |
40 | :Version: Id: portalocker.py,v 1.3 2001/05/29 18:47:55 Administrator Exp | |
41 | **un-cvsified by richard so the version doesn't change** | |
42 | """ | |
43 | __docformat__ = 'restructuredtext' | |
44 | ||
45 | import os | |
46 | ||
47 | if os.name == 'nt': | |
48 | import win32con | |
49 | import win32file | |
50 | import pywintypes | |
51 | LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK | |
52 | LOCK_SH = 0 # the default | |
53 | LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY | |
54 | # is there any reason not to reuse the following structure? | |
55 | __overlapped = pywintypes.OVERLAPPED() | |
56 | elif os.name == 'posix': | |
57 | import fcntl | |
58 | LOCK_EX = fcntl.LOCK_EX | |
59 | LOCK_SH = fcntl.LOCK_SH | |
60 | LOCK_NB = fcntl.LOCK_NB | |
61 | else: | |
62 | raise RuntimeError("PortaLocker only defined for nt and posix platforms") | |
63 | ||
64 | if os.name == 'nt': | |
65 | # eugh, we want 0xffff0000 here, but python 2.3 won't let us :( | |
66 | FFFF0000 = -65536 | |
67 | def lock(file, flags): | |
68 | hfile = win32file._get_osfhandle(file.fileno()) | |
69 | # LockFileEx is not supported on all Win32 platforms (Win95, Win98, | |
70 | # WinME). | |
71 | # If it's not supported, win32file will raise an exception. | |
72 | # Try LockFileEx first, as it has more functionality and handles | |
73 | # blocking locks more efficiently. | |
74 | try: | |
75 | win32file.LockFileEx(hfile, flags, 0, FFFF0000, __overlapped) | |
76 | except win32file.error, e: | |
77 | import winerror | |
78 | # Propagate upwards all exceptions other than not-implemented. | |
79 | if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED: | |
80 | raise e | |
81 | ||
82 | # LockFileEx is not supported. Use LockFile. | |
83 | # LockFile does not support shared locking -- always exclusive. | |
84 | # Care: the low/high length params are reversed compared to | |
85 | # LockFileEx. | |
86 | if not flags & LOCK_EX: | |
87 | import warnings | |
88 | warnings.warn("PortaLocker does not support shared " | |
89 | "locking on Win9x", RuntimeWarning) | |
90 | # LockFile only supports immediate-fail locking. | |
91 | if flags & LOCK_NB: | |
92 | win32file.LockFile(hfile, 0, 0, FFFF0000, 0) | |
93 | else: | |
94 | # Emulate a blocking lock with a polling loop. | |
95 | import time | |
96 | while 1: | |
97 | # Attempt a lock. | |
98 | try: | |
99 | win32file.LockFile(hfile, 0, 0, FFFF0000, 0) | |
100 | break | |
101 | except win32file.error, e: | |
102 | # Propagate upwards all exceptions other than lock | |
103 | # violation. | |
104 | if e[0] != winerror.ERROR_LOCK_VIOLATION: | |
105 | raise e | |
106 | # Sleep and poll again. | |
107 | time.sleep(0.1) | |
108 | # TODO: should this return the result of the lock? | |
109 | ||
110 | def unlock(file): | |
111 | hfile = win32file._get_osfhandle(file.fileno()) | |
112 | # UnlockFileEx is not supported on all Win32 platforms (Win95, Win98, | |
113 | # WinME). | |
114 | # If it's not supported, win32file will raise an api_error exception. | |
115 | try: | |
116 | win32file.UnlockFileEx(hfile, 0, FFFF0000, __overlapped) | |
117 | except win32file.error, e: | |
118 | import winerror | |
119 | # Propagate upwards all exceptions other than not-implemented. | |
120 | if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED: | |
121 | raise e | |
122 | ||
123 | # UnlockFileEx is not supported. Use UnlockFile. | |
124 | # Care: the low/high length params are reversed compared to | |
125 | # UnLockFileEx. | |
126 | win32file.UnlockFile(hfile, 0, 0, FFFF0000, 0) | |
127 | ||
128 | elif os.name =='posix': | |
129 | def lock(file, flags): | |
130 | fcntl.flock(file.fileno(), flags) | |
131 | # TODO: should this return the result of the lock? | |
132 | ||
133 | def unlock(file): | |
134 | fcntl.flock(file.fileno(), fcntl.LOCK_UN) | |
135 | ||
136 | if __name__ == '__main__': | |
137 | from time import time, strftime, localtime | |
138 | import sys | |
139 | ||
140 | log = open('log.txt', "a+") | |
141 | lock(log, LOCK_EX) | |
142 | ||
143 | timestamp = strftime("%m/%d/%Y %H:%M:%S\n", localtime(time())) | |
144 | log.write( timestamp ) | |
145 | ||
146 | print "Wrote lines. Hit enter to release lock." | |
147 | dummy = sys.stdin.readline() | |
148 | ||
149 | log.close() | |
150 |