Skip to content

Commit 1a131fd

Browse files
committed
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.
1 parent 46559ea commit 1a131fd

File tree

3 files changed

+86
-73
lines changed

3 files changed

+86
-73
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Entries without name were done by Richard Jones.
77

88
Features:
99

10+
- pywin32 is not longer required to run on Windows (anatoly techtonik)
11+
- Rewritten portalocker.py logic in ctypes for Windows (anatoly techtonik)
1012
- Add an interface to register clearCache callbacks in roundupdb.
1113
Sometimes complicated computations may require an application cache.
1214
This application can now register a callback to clear the application

doc/installation.txt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ Some variants of Linux will need an additional "python dev" package
3939
installed for Roundup installation to work. Debian and derivatives, are
4040
known to require this.
4141

42-
If you're on windows, you will either need to be using the ActiveState python
43-
distribution (at http://www.activestate.com/Products/ActivePython/), or you'll
44-
have to install the win32all package separately (get it from
45-
http://starship.python.net/crew/mhammond/win32/).
46-
4742

4843
Optional Components
4944
===================
@@ -83,11 +78,15 @@ pyme
8378
configured, you can require email to be cryptographically signed
8479
before roundup will allow it to make modifications to issues.
8580

81+
Windows Service
82+
You can run Roundup as a Windows service if pywin32_ is installed.
83+
8684
.. _Xapian: http://xapian.org/
8785
.. _pytz: http://www.python.org/pypi/pytz
8886
.. _Olson tz database: http://www.twinsun.com/tz/tz-link.htm
8987
.. _pyopenssl: http://pyopenssl.sourceforge.net
9088
.. _pyme: http://pyme.sourceforge.net
89+
.. _pywin32: http://pypi.python.org/pypi/pywin32
9190

9291

9392
Getting Roundup

roundup/backends/portalocker.py

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
#!/usr/bin/env python
2+
#
13
# portalocker.py - Cross-platform (posix/nt) API for flock-style file locking.
2-
# Requires python 1.5.2 or better.
3-
4-
"""Cross-platform (posix/nt) API for flock-style file locking.
4+
#
5+
# http://code.activestate.com/recipes/65203-portalocker-cross-platform-posixnt-api-for-flock-s/
6+
#
7+
"""
8+
Cross-platform (posix/nt) API for flock-style file locking.
59
610
Synopsis::
711
@@ -34,22 +38,69 @@
3438
that accompanies the win32 modules.
3539
3640
:Author: Jonathan Feinberg <[email protected]>
37-
:Version: Id: portalocker.py,v 1.3 2001/05/29 18:47:55 Administrator Exp
38-
**un-cvsified by richard so the version doesn't change**
41+
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
3948
"""
49+
4050
__docformat__ = 'restructuredtext'
4151

4252
import os
4353

4454
if os.name == 'nt':
45-
import win32file
46-
import pywintypes
4755
import msvcrt
56+
from ctypes import *
57+
from ctypes.wintypes import BOOL, DWORD, HANDLE
58+
4859
LOCK_SH = 0 # the default
4960
LOCK_NB = 0x1 # LOCKFILE_FAIL_IMMEDIATELY
5061
LOCK_EX = 0x2 # LOCKFILE_EXCLUSIVE_LOCK
51-
# is there any reason not to reuse the following structure?
52-
__overlapped = pywintypes.OVERLAPPED()
62+
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+
53104
elif os.name == 'posix':
54105
import fcntl
55106
LOCK_SH = fcntl.LOCK_SH # shared lock
@@ -60,73 +111,34 @@
60111

61112
if os.name == 'nt':
62113
def lock(file, flags):
114+
""" Return True on success, False otherwise """
63115
hfile = msvcrt.get_osfhandle(file.fileno())
64-
# LockFileEx is not supported on all Win32 platforms (Win95, Win98,
65-
# WinME).
66-
# If it's not supported, win32file will raise an exception.
67-
# Try LockFileEx first, as it has more functionality and handles
68-
# blocking locks more efficiently.
69-
try:
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-
116+
overlapped = OVERLAPPED()
117+
if LockFileEx(hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped)):
118+
return True
119+
else:
120+
return False
121+
105122
def unlock(file):
106123
hfile = msvcrt.get_osfhandle(file.fileno())
107-
# UnlockFileEx is not supported on all Win32 platforms (Win95, Win98,
108-
# WinME).
109-
# If it's not supported, win32file will raise an api_error exception.
110-
try:
111-
win32file.UnlockFileEx(hfile, 0, 0xFFFF0000, __overlapped)
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)
124+
overlapped = OVERLAPPED()
125+
if UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped)):
126+
return True
127+
else:
128+
return False
122129

123130
elif os.name =='posix':
124131
def lock(file, flags):
125-
fcntl.flock(file.fileno(), flags)
126-
# TODO: should this return the result of the lock?
132+
if fcntl.flock(file.fileno(), flags) == 0:
133+
return True
134+
else:
135+
return False
127136

128137
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
130142

131143
if __name__ == '__main__':
132144
from time import time, strftime, localtime

0 commit comments

Comments
 (0)