comparison roundup/backends/portalocker.py @ 4686:4e740f02e165

Remove pywin32 installation dependency by porting portalocker.py to ctypes. portalocker.py lock/unlock functions now return result of operation, support for Windows 95/98/ME is removed.
author anatoly techtonik <techtonik@gmail.com>
date Wed, 28 Nov 2012 04:51:56 +0300
parents 6c74a8bff423
children d27de8f08c31
comparison
equal deleted inserted replaced
4685:61e922a93112 4686:4e740f02e165
1 #!/usr/bin/env python
2 #
1 # portalocker.py - Cross-platform (posix/nt) API for flock-style file locking. 3 # portalocker.py - Cross-platform (posix/nt) API for flock-style file locking.
2 # Requires python 1.5.2 or better. 4 #
3 5 # http://code.activestate.com/recipes/65203-portalocker-cross-platform-posixnt-api-for-flock-s/
4 """Cross-platform (posix/nt) API for flock-style file locking. 6 #
7 """
8 Cross-platform (posix/nt) API for flock-style file locking.
5 9
6 Synopsis:: 10 Synopsis::
7 11
8 import portalocker 12 import portalocker
9 file = open("somefile", "r+") 13 file = open("somefile", "r+")
32 I learned the win32 technique for locking files from sample code 36 I learned the win32 technique for locking files from sample code
33 provided by John Nielsen <nielsenjf@my-deja.com> in the documentation 37 provided by John Nielsen <nielsenjf@my-deja.com> in the documentation
34 that accompanies the win32 modules. 38 that accompanies the win32 modules.
35 39
36 :Author: Jonathan Feinberg <jdf@pobox.com> 40 :Author: Jonathan Feinberg <jdf@pobox.com>
37 :Version: Id: portalocker.py,v 1.3 2001/05/29 18:47:55 Administrator Exp 41
38 **un-cvsified by richard so the version doesn't change** 42 Roundup Changes
43 ---------------
44 2012-11-28 (anatoly techtonik)
45 - Ported to ctypes
46 - Dropped support for Win95, Win98 and WinME
47 - Added return result
39 """ 48 """
49
40 __docformat__ = 'restructuredtext' 50 __docformat__ = 'restructuredtext'
41 51
42 import os 52 import os
43 53
44 if os.name == 'nt': 54 if os.name == 'nt':
45 import win32file
46 import pywintypes
47 import msvcrt 55 import msvcrt
56 from ctypes import *
57 from ctypes.wintypes import BOOL, DWORD, HANDLE
58
48 LOCK_SH = 0 # the default 59 LOCK_SH = 0 # the default
49 LOCK_NB = 0x1 # LOCKFILE_FAIL_IMMEDIATELY 60 LOCK_NB = 0x1 # LOCKFILE_FAIL_IMMEDIATELY
50 LOCK_EX = 0x2 # LOCKFILE_EXCLUSIVE_LOCK 61 LOCK_EX = 0x2 # LOCKFILE_EXCLUSIVE_LOCK
51 # is there any reason not to reuse the following structure? 62
52 __overlapped = pywintypes.OVERLAPPED() 63 # --- the code is taken from pyserial project ---
64 #
65 # detect size of ULONG_PTR
66 def is_64bit():
67 return sizeof(c_ulong) != sizeof(c_void_p)
68 if is_64bit():
69 ULONG_PTR = c_int64
70 else:
71 ULONG_PTR = c_ulong
72 PVOID = c_void_p
73
74 # --- Union inside Structure by stackoverflow:3480240 ---
75 class _OFFSET(Structure):
76 _fields_ = [
77 ('Offset', DWORD),
78 ('OffsetHigh', DWORD)]
79
80 class _OFFSET_UNION(Union):
81 _anonymous_ = ['_offset']
82 _fields_ = [
83 ('_offset', _OFFSET),
84 ('Pointer', PVOID)]
85
86 class OVERLAPPED(Structure):
87 _anonymous_ = ['_offset_union']
88 _fields_ = [
89 ('Internal', ULONG_PTR),
90 ('InternalHigh', ULONG_PTR),
91 ('_offset_union', _OFFSET_UNION),
92 ('hEvent', HANDLE)]
93
94 LPOVERLAPPED = POINTER(OVERLAPPED)
95
96 # --- Define function prototypes for extra safety ---
97 LockFileEx = windll.kernel32.LockFileEx
98 LockFileEx.restype = BOOL
99 LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED]
100 UnlockFileEx = windll.kernel32.UnlockFileEx
101 UnlockFileEx.restype = BOOL
102 UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]
103
53 elif os.name == 'posix': 104 elif os.name == 'posix':
54 import fcntl 105 import fcntl
55 LOCK_SH = fcntl.LOCK_SH # shared lock 106 LOCK_SH = fcntl.LOCK_SH # shared lock
56 LOCK_NB = fcntl.LOCK_NB # non-blocking 107 LOCK_NB = fcntl.LOCK_NB # non-blocking
57 LOCK_EX = fcntl.LOCK_EX 108 LOCK_EX = fcntl.LOCK_EX
58 else: 109 else:
59 raise RuntimeError("PortaLocker only defined for nt and posix platforms") 110 raise RuntimeError("PortaLocker only defined for nt and posix platforms")
60 111
61 if os.name == 'nt': 112 if os.name == 'nt':
62 def lock(file, flags): 113 def lock(file, flags):
114 """ Return True on success, False otherwise """
63 hfile = msvcrt.get_osfhandle(file.fileno()) 115 hfile = msvcrt.get_osfhandle(file.fileno())
64 # LockFileEx is not supported on all Win32 platforms (Win95, Win98, 116 overlapped = OVERLAPPED()
65 # WinME). 117 if LockFileEx(hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped)):
66 # If it's not supported, win32file will raise an exception. 118 return True
67 # Try LockFileEx first, as it has more functionality and handles 119 else:
68 # blocking locks more efficiently. 120 return False
69 try: 121
70 win32file.LockFileEx(hfile, flags, 0, 0xFFFF0000, __overlapped)
71 except win32file.error, e:
72 import winerror
73 # Propagate upwards all exceptions other than not-implemented.
74 if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED:
75 raise e
76
77 # LockFileEx is not supported. Use LockFile.
78 # LockFile does not support shared locking -- always exclusive.
79 # Care: the low/high length params are reversed compared to
80 # LockFileEx.
81 if not flags & LOCK_EX:
82 import warnings
83 warnings.warn("PortaLocker does not support shared "
84 "locking on Win9x", RuntimeWarning)
85 # LockFile only supports immediate-fail locking.
86 if flags & LOCK_NB:
87 win32file.LockFile(hfile, 0, 0, 0xFFFF0000, 0)
88 else:
89 # Emulate a blocking lock with a polling loop.
90 import time
91 while 1:
92 # Attempt a lock.
93 try:
94 win32file.LockFile(hfile, 0, 0, 0xFFFF0000, 0)
95 break
96 except win32file.error, e:
97 # Propagate upwards all exceptions other than lock
98 # violation.
99 if e[0] != winerror.ERROR_LOCK_VIOLATION:
100 raise e
101 # Sleep and poll again.
102 time.sleep(0.1)
103 # TODO: should this return the result of the lock?
104
105 def unlock(file): 122 def unlock(file):
106 hfile = msvcrt.get_osfhandle(file.fileno()) 123 hfile = msvcrt.get_osfhandle(file.fileno())
107 # UnlockFileEx is not supported on all Win32 platforms (Win95, Win98, 124 overlapped = OVERLAPPED()
108 # WinME). 125 if UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped)):
109 # If it's not supported, win32file will raise an api_error exception. 126 return True
110 try: 127 else:
111 win32file.UnlockFileEx(hfile, 0, 0xFFFF0000, __overlapped) 128 return False
112 except win32file.error, e:
113 import winerror
114 # Propagate upwards all exceptions other than not-implemented.
115 if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED:
116 raise e
117
118 # UnlockFileEx is not supported. Use UnlockFile.
119 # Care: the low/high length params are reversed compared to
120 # UnLockFileEx.
121 win32file.UnlockFile(hfile, 0, 0, 0xFFFF0000, 0)
122 129
123 elif os.name =='posix': 130 elif os.name =='posix':
124 def lock(file, flags): 131 def lock(file, flags):
125 fcntl.flock(file.fileno(), flags) 132 if fcntl.flock(file.fileno(), flags) == 0:
126 # TODO: should this return the result of the lock? 133 return True
134 else:
135 return False
127 136
128 def unlock(file): 137 def unlock(file):
129 fcntl.flock(file.fileno(), fcntl.LOCK_UN) 138 if fcntl.flock(file.fileno(), fcntl.LOCK_UN) == 0:
139 return True
140 else:
141 return False
130 142
131 if __name__ == '__main__': 143 if __name__ == '__main__':
132 from time import time, strftime, localtime 144 from time import time, strftime, localtime
133 import sys 145 import sys
134 146

Roundup Issue Tracker: http://roundup-tracker.org/