changeset 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 61e922a93112
children 4960a2c21590
files CHANGES.txt doc/installation.txt roundup/backends/portalocker.py
diffstat 3 files changed, 86 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Wed Nov 28 03:44:34 2012 +0300
+++ b/CHANGES.txt	Wed Nov 28 04:51:56 2012 +0300
@@ -7,6 +7,8 @@
 
 Features:
 
+- pywin32 is not longer required to run on Windows (anatoly techtonik)
+- Rewritten portalocker.py logic in ctypes for Windows (anatoly techtonik)
 - Add an interface to register clearCache callbacks in roundupdb.
   Sometimes complicated computations may require an application cache.
   This application can now register a callback to clear the application
--- a/doc/installation.txt	Wed Nov 28 03:44:34 2012 +0300
+++ b/doc/installation.txt	Wed Nov 28 04:51:56 2012 +0300
@@ -39,11 +39,6 @@
 installed for Roundup installation to work. Debian and derivatives, are
 known to require this.
 
-If you're on windows, you will either need to be using the ActiveState python
-distribution (at http://www.activestate.com/Products/ActivePython/), or you'll
-have to install the win32all package separately (get it from
-http://starship.python.net/crew/mhammond/win32/).
-
 
 Optional Components
 ===================
@@ -83,11 +78,15 @@
   configured, you can require email to be cryptographically signed
   before roundup will allow it to make modifications to issues.
 
+Windows Service
+  You can run Roundup as a Windows service if pywin32_ is installed.
+
 .. _Xapian: http://xapian.org/
 .. _pytz: http://www.python.org/pypi/pytz
 .. _Olson tz database: http://www.twinsun.com/tz/tz-link.htm
 .. _pyopenssl: http://pyopenssl.sourceforge.net
 .. _pyme: http://pyme.sourceforge.net
+.. _pywin32: http://pypi.python.org/pypi/pywin32
 
 
 Getting Roundup
--- a/roundup/backends/portalocker.py	Wed Nov 28 03:44:34 2012 +0300
+++ b/roundup/backends/portalocker.py	Wed Nov 28 04:51:56 2012 +0300
@@ -1,7 +1,11 @@
+#!/usr/bin/env python
+#
 # portalocker.py - Cross-platform (posix/nt) API for flock-style file locking.
-#                  Requires python 1.5.2 or better.
-
-"""Cross-platform (posix/nt) API for flock-style file locking.
+#
+# http://code.activestate.com/recipes/65203-portalocker-cross-platform-posixnt-api-for-flock-s/
+#
+"""
+Cross-platform (posix/nt) API for flock-style file locking.
 
 Synopsis::
 
@@ -34,22 +38,69 @@
 that accompanies the win32 modules.
 
 :Author: Jonathan Feinberg <jdf@pobox.com>
-:Version: Id: portalocker.py,v 1.3 2001/05/29 18:47:55 Administrator Exp 
-          **un-cvsified by richard so the version doesn't change**
+
+Roundup Changes
+---------------
+2012-11-28 (anatoly techtonik)
+   - Ported to ctypes
+   - Dropped support for Win95, Win98 and WinME
+   - Added return result
 """
+
 __docformat__ = 'restructuredtext'
 
 import os
 
 if os.name == 'nt':
-    import win32file
-    import pywintypes
     import msvcrt
+    from ctypes import *
+    from ctypes.wintypes import BOOL, DWORD, HANDLE
+
     LOCK_SH = 0    # the default
     LOCK_NB = 0x1  # LOCKFILE_FAIL_IMMEDIATELY
     LOCK_EX = 0x2  # LOCKFILE_EXCLUSIVE_LOCK
-    # is there any reason not to reuse the following structure?
-    __overlapped = pywintypes.OVERLAPPED()
+
+    # --- the code is taken from pyserial project ---
+    #
+    # detect size of ULONG_PTR 
+    def is_64bit():
+        return sizeof(c_ulong) != sizeof(c_void_p)
+    if is_64bit():
+        ULONG_PTR = c_int64
+    else:
+        ULONG_PTR = c_ulong
+    PVOID = c_void_p
+
+    # --- Union inside Structure by stackoverflow:3480240 ---
+    class _OFFSET(Structure):
+        _fields_ = [
+            ('Offset', DWORD),
+            ('OffsetHigh', DWORD)]
+
+    class _OFFSET_UNION(Union):
+        _anonymous_ = ['_offset']
+        _fields_ = [
+            ('_offset', _OFFSET),
+            ('Pointer', PVOID)]
+
+    class OVERLAPPED(Structure):
+        _anonymous_ = ['_offset_union']
+        _fields_ = [
+            ('Internal', ULONG_PTR),
+            ('InternalHigh', ULONG_PTR),
+            ('_offset_union', _OFFSET_UNION),
+            ('hEvent', HANDLE)]
+
+    LPOVERLAPPED = POINTER(OVERLAPPED)
+
+    # --- Define function prototypes for extra safety ---
+    LockFileEx = windll.kernel32.LockFileEx
+    LockFileEx.restype = BOOL
+    LockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED]
+    UnlockFileEx = windll.kernel32.UnlockFileEx
+    UnlockFileEx.restype = BOOL
+    UnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]
+            
 elif os.name == 'posix':
     import fcntl
     LOCK_SH = fcntl.LOCK_SH  # shared lock
@@ -60,73 +111,34 @@
 
 if os.name == 'nt':
     def lock(file, flags):
+        """ Return True on success, False otherwise """
         hfile = msvcrt.get_osfhandle(file.fileno())
-        # LockFileEx is not supported on all Win32 platforms (Win95, Win98,
-        # WinME).
-        # If it's not supported, win32file will raise an exception.
-        # Try LockFileEx first, as it has more functionality and handles
-        # blocking locks more efficiently.
-        try:
-            win32file.LockFileEx(hfile, flags, 0, 0xFFFF0000, __overlapped)
-        except win32file.error, e:
-            import winerror
-            # Propagate upwards all exceptions other than not-implemented.
-            if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED:
-                raise e
-            
-            # LockFileEx is not supported. Use LockFile.
-            # LockFile does not support shared locking -- always exclusive.
-            # Care: the low/high length params are reversed compared to
-            # LockFileEx.
-            if not flags & LOCK_EX:
-                import warnings
-                warnings.warn("PortaLocker does not support shared "
-                    "locking on Win9x", RuntimeWarning)
-            # LockFile only supports immediate-fail locking.
-            if flags & LOCK_NB:
-                win32file.LockFile(hfile, 0, 0, 0xFFFF0000, 0)
-            else:
-                # Emulate a blocking lock with a polling loop.
-                import time
-                while 1:
-                    # Attempt a lock.
-                    try:
-                        win32file.LockFile(hfile, 0, 0, 0xFFFF0000, 0)
-                        break
-                    except win32file.error, e:
-                        # Propagate upwards all exceptions other than lock
-                        # violation.
-                        if e[0] != winerror.ERROR_LOCK_VIOLATION:
-                            raise e
-                    # Sleep and poll again.
-                    time.sleep(0.1)
-        # TODO: should this return the result of the lock?
-                    
+        overlapped = OVERLAPPED()
+        if LockFileEx(hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped)):
+            return True
+        else:
+            return False
+
     def unlock(file):
         hfile = msvcrt.get_osfhandle(file.fileno())
-        # UnlockFileEx is not supported on all Win32 platforms (Win95, Win98,
-        # WinME).
-        # If it's not supported, win32file will raise an api_error exception.
-        try:
-            win32file.UnlockFileEx(hfile, 0, 0xFFFF0000, __overlapped)
-        except win32file.error, e:
-            import winerror
-            # Propagate upwards all exceptions other than not-implemented.
-            if e[0] != winerror.ERROR_CALL_NOT_IMPLEMENTED:
-                raise e
-            
-            # UnlockFileEx is not supported. Use UnlockFile.
-            # Care: the low/high length params are reversed compared to
-            # UnLockFileEx.
-            win32file.UnlockFile(hfile, 0, 0, 0xFFFF0000, 0)
+        overlapped = OVERLAPPED()
+        if UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped)):
+            return True
+        else:
+            return False
 
 elif os.name =='posix':
     def lock(file, flags):
-        fcntl.flock(file.fileno(), flags)
-        # TODO: should this return the result of the lock?
+        if fcntl.flock(file.fileno(), flags) == 0:
+            return True
+        else:
+            return False
 
     def unlock(file):
-        fcntl.flock(file.fileno(), fcntl.LOCK_UN)
+        if fcntl.flock(file.fileno(), fcntl.LOCK_UN) == 0:
+            return True
+        else:
+            return False
 
 if __name__ == '__main__':
     from time import time, strftime, localtime

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