diff --git a/.gitignore b/.gitignore index d35cddebd..a672c3f16 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ nbproject .DS_Store /*egg-info /.tox +.cache diff --git a/AUTHORS b/AUTHORS index e2c3293cc..b06c5b46f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,5 +15,6 @@ Contributors are: -Jonathan Chu -Vincent Driessen -Phil Elson +-Daniele Esposti Portions derived from other open source works and are clearly marked. diff --git a/git/test/test_util.py b/git/test/test_util.py index e07417b4b..6ba3d0d40 100644 --- a/git/test/test_util.py +++ b/git/test/test_util.py @@ -5,6 +5,7 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php import tempfile +import gc from git.test.lib import ( TestBase, @@ -80,6 +81,7 @@ def test_lock_file(self): # auto-release on destruction del(other_lock_file) + gc.collect() lock_file._obtain_lock_or_raise() lock_file._release_lock() diff --git a/git/util.py b/git/util.py index c96a6b087..037fd91b5 100644 --- a/git/util.py +++ b/git/util.py @@ -48,6 +48,56 @@ #{ Utility Methods +if platform.system() == 'Windows': + # This code is a derivative work of Portalocker http://code.activestate.com/recipes/65203/ + import win32con + import win32file + import pywintypes + + LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK + LOCK_SH = 0 # the default + LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY + LOCK_UN = 1 << 2 + + __overlapped = pywintypes.OVERLAPPED() + + def flock(fd, flags=0): + hfile = win32file._get_osfhandle(fd) + + if flags & LOCK_UN != 0: + # Unlock file descriptor + try: + win32file.UnlockFileEx(hfile, 0, -0x10000, __overlapped) + except pywintypes.error as exc_value: + # error: (158, 'UnlockFileEx', 'The segment is already unlocked.') + # To match the 'posix' implementation, silently ignore this error + if exc_value[0] == 158: + pass + else: + # Q: Are there exceptions/codes we should be dealing with here? + raise + + elif flags & LOCK_EX != 0: + # Lock file + try: + win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped) + except pywintypes.error as exc_value: + if exc_value[0] == 33: + # error: (33, 'LockFileEx', + # 'The process cannot access the file because another process has locked + # a portion of the file.') + raise IOError(33, exc_value[2]) + else: + # Q: Are there exceptions/codes we should be dealing with here? + raise + + else: + raise NotImplementedError("Unsupported set of bitflags {}".format(bin(flags))) + + +else: + from fcntl import flock, LOCK_UN, LOCK_EX, LOCK_NB + def unbare_repo(func): """Methods with this decorator raise InvalidGitRepositoryError if they @@ -555,9 +605,10 @@ class LockFile(object): As we are a utility class to be derived from, we only use protected methods. Locks will automatically be released on destruction""" - __slots__ = ("_file_path", "_owns_lock") + __slots__ = ("_file_path", "_owns_lock", "_file_descriptor") def __init__(self, file_path): + self._file_descriptor = None self._file_path = file_path self._owns_lock = False @@ -579,20 +630,21 @@ def _obtain_lock_or_raise(self): :raise IOError: if a lock was already present or a lock file could not be written""" if self._has_lock(): return + lock_file = self._lock_file_path() - if os.path.isfile(lock_file): - raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" % - (self._file_path, lock_file)) + # Create file and lock try: - flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL + flags = os.O_CREAT if is_win: flags |= os.O_SHORT_LIVED fd = os.open(lock_file, flags, 0) - os.close(fd) except OSError as e: raise IOError(str(e)) + flock(fd, LOCK_EX | LOCK_NB) + + self._file_descriptor = fd self._owns_lock = True def _obtain_lock(self): @@ -605,14 +657,21 @@ def _release_lock(self): if not self._has_lock(): return + fd = self._file_descriptor + lock_file = self._lock_file_path() + + flock(fd, LOCK_UN) + os.close(fd) + # if someone removed our file beforhand, lets just flag this issue # instead of failing, to make it more usable. - lfp = self._lock_file_path() try: - rmfile(lfp) + rmfile(lock_file) except OSError: pass + self._owns_lock = False + self._file_descriptor = None class BlockingLockFile(LockFile): @@ -647,7 +706,7 @@ def _obtain_lock(self): try: super(BlockingLockFile, self)._obtain_lock() except IOError: - # synity check: if the directory leading to the lockfile is not + # sanity check: if the directory leading to the lockfile is not # readable anymore, raise an execption curtime = time.time() if not os.path.isdir(os.path.dirname(self._lock_file_path())): diff --git a/setup.py b/setup.py index c7dd25fcc..43b0505bd 100755 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ import logging import os import sys +import platform from os import path with open(path.join(path.dirname(__file__), 'VERSION')) as v: @@ -21,6 +22,10 @@ with open('requirements.txt') as reqs_file: requirements = reqs_file.read().splitlines() +if platform.system() == 'Windows': + with open('win32-requirements.txt') as reqs_file: + requirements += reqs_file.read().splitlines() + class build_py(_build_py): @@ -65,6 +70,8 @@ def _stamp_version(filename): print("WARNING: Couldn't find version line in file %s" % filename, file=sys.stderr) install_requires = ['gitdb >= 0.6.4'] +if platform.system() == 'Windows': + install_requires.append("pypiwin32 >= 219") extras_require = { ':python_version == "2.6"': ['ordereddict'], } diff --git a/win32-requirements.txt b/win32-requirements.txt new file mode 100644 index 000000000..0008dcffd --- /dev/null +++ b/win32-requirements.txt @@ -0,0 +1,2 @@ +-r requirements.txt +pypiwin32 >= 219 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy