4848
4949#{ Utility Methods
5050
51+ if platform .system () == 'Windows' :
52+ # This code is a derivative work of Portalocker http://code.activestate.com/recipes/65203/
53+ import win32con
54+ import win32file
55+ import pywintypes
56+
57+ LOCK_EX = win32con .LOCKFILE_EXCLUSIVE_LOCK
58+ LOCK_SH = 0 # the default
59+ LOCK_NB = win32con .LOCKFILE_FAIL_IMMEDIATELY
60+ LOCK_UN = 1 << 2
61+
62+ __overlapped = pywintypes .OVERLAPPED ()
63+
64+ def flock (fd , flags = 0 ):
65+ hfile = win32file ._get_osfhandle (fd )
66+
67+ if flags & LOCK_UN != 0 :
68+ # Unlock file descriptor
69+ try :
70+ win32file .UnlockFileEx (hfile , 0 , - 0x10000 , __overlapped )
71+ except pywintypes .error as exc_value :
72+ # error: (158, 'UnlockFileEx', 'The segment is already unlocked.')
73+ # To match the 'posix' implementation, silently ignore this error
74+ if exc_value [0 ] == 158 :
75+ pass
76+ else :
77+ # Q: Are there exceptions/codes we should be dealing with here?
78+ raise
79+
80+ elif flags & LOCK_EX != 0 :
81+ # Lock file
82+ try :
83+ win32file .LockFileEx (hfile , flags , 0 , - 0x10000 , __overlapped )
84+ except pywintypes .error as exc_value :
85+ if exc_value [0 ] == 33 :
86+ # error: (33, 'LockFileEx',
87+ # 'The process cannot access the file because another process has locked
88+ # a portion of the file.')
89+ raise IOError (33 , exc_value [2 ])
90+ else :
91+ # Q: Are there exceptions/codes we should be dealing with here?
92+ raise
93+
94+ else :
95+ raise NotImplementedError ("Unsupported set of bitflags {}" .format (bin (flags )))
96+
97+
98+ else :
99+ from fcntl import flock , LOCK_UN , LOCK_EX , LOCK_NB
100+
51101
52102def unbare_repo (func ):
53103 """Methods with this decorator raise InvalidGitRepositoryError if they
@@ -555,9 +605,10 @@ class LockFile(object):
555605 As we are a utility class to be derived from, we only use protected methods.
556606
557607 Locks will automatically be released on destruction"""
558- __slots__ = ("_file_path" , "_owns_lock" )
608+ __slots__ = ("_file_path" , "_owns_lock" , "_file_descriptor" )
559609
560610 def __init__ (self , file_path ):
611+ self ._file_descriptor = None
561612 self ._file_path = file_path
562613 self ._owns_lock = False
563614
@@ -579,20 +630,21 @@ def _obtain_lock_or_raise(self):
579630 :raise IOError: if a lock was already present or a lock file could not be written"""
580631 if self ._has_lock ():
581632 return
633+
582634 lock_file = self ._lock_file_path ()
583- if os .path .isfile (lock_file ):
584- raise IOError ("Lock for file %r did already exist, delete %r in case the lock is illegal" %
585- (self ._file_path , lock_file ))
586635
636+ # Create file and lock
587637 try :
588- flags = os .O_WRONLY | os . O_CREAT | os . O_EXCL
638+ flags = os .O_CREAT
589639 if is_win :
590640 flags |= os .O_SHORT_LIVED
591641 fd = os .open (lock_file , flags , 0 )
592- os .close (fd )
593642 except OSError as e :
594643 raise IOError (str (e ))
595644
645+ flock (fd , LOCK_EX | LOCK_NB )
646+
647+ self ._file_descriptor = fd
596648 self ._owns_lock = True
597649
598650 def _obtain_lock (self ):
@@ -605,14 +657,21 @@ def _release_lock(self):
605657 if not self ._has_lock ():
606658 return
607659
660+ fd = self ._file_descriptor
661+ lock_file = self ._lock_file_path ()
662+
663+ flock (fd , LOCK_UN )
664+ os .close (fd )
665+
608666 # if someone removed our file beforhand, lets just flag this issue
609667 # instead of failing, to make it more usable.
610- lfp = self ._lock_file_path ()
611668 try :
612- rmfile (lfp )
669+ rmfile (lock_file )
613670 except OSError :
614671 pass
672+
615673 self ._owns_lock = False
674+ self ._file_descriptor = None
616675
617676
618677class BlockingLockFile (LockFile ):
@@ -647,7 +706,7 @@ def _obtain_lock(self):
647706 try :
648707 super (BlockingLockFile , self )._obtain_lock ()
649708 except IOError :
650- # synity check: if the directory leading to the lockfile is not
709+ # sanity check: if the directory leading to the lockfile is not
651710 # readable anymore, raise an execption
652711 curtime = time .time ()
653712 if not os .path .isdir (os .path .dirname (self ._lock_file_path ())):
0 commit comments