48
48
49
49
#{ Utility Methods
50
50
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
+
51
101
52
102
def unbare_repo (func ):
53
103
"""Methods with this decorator raise InvalidGitRepositoryError if they
@@ -555,9 +605,10 @@ class LockFile(object):
555
605
As we are a utility class to be derived from, we only use protected methods.
556
606
557
607
Locks will automatically be released on destruction"""
558
- __slots__ = ("_file_path" , "_owns_lock" )
608
+ __slots__ = ("_file_path" , "_owns_lock" , "_file_descriptor" )
559
609
560
610
def __init__ (self , file_path ):
611
+ self ._file_descriptor = None
561
612
self ._file_path = file_path
562
613
self ._owns_lock = False
563
614
@@ -579,20 +630,21 @@ def _obtain_lock_or_raise(self):
579
630
:raise IOError: if a lock was already present or a lock file could not be written"""
580
631
if self ._has_lock ():
581
632
return
633
+
582
634
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 ))
586
635
636
+ # Create file and lock
587
637
try :
588
- flags = os .O_WRONLY | os . O_CREAT | os . O_EXCL
638
+ flags = os .O_CREAT
589
639
if is_win :
590
640
flags |= os .O_SHORT_LIVED
591
641
fd = os .open (lock_file , flags , 0 )
592
- os .close (fd )
593
642
except OSError as e :
594
643
raise IOError (str (e ))
595
644
645
+ flock (fd , LOCK_EX | LOCK_NB )
646
+
647
+ self ._file_descriptor = fd
596
648
self ._owns_lock = True
597
649
598
650
def _obtain_lock (self ):
@@ -605,14 +657,21 @@ def _release_lock(self):
605
657
if not self ._has_lock ():
606
658
return
607
659
660
+ fd = self ._file_descriptor
661
+ lock_file = self ._lock_file_path ()
662
+
663
+ flock (fd , LOCK_UN )
664
+ os .close (fd )
665
+
608
666
# if someone removed our file beforhand, lets just flag this issue
609
667
# instead of failing, to make it more usable.
610
- lfp = self ._lock_file_path ()
611
668
try :
612
- rmfile (lfp )
669
+ rmfile (lock_file )
613
670
except OSError :
614
671
pass
672
+
615
673
self ._owns_lock = False
674
+ self ._file_descriptor = None
616
675
617
676
618
677
class BlockingLockFile (LockFile ):
@@ -647,7 +706,7 @@ def _obtain_lock(self):
647
706
try :
648
707
super (BlockingLockFile , self )._obtain_lock ()
649
708
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
651
710
# readable anymore, raise an execption
652
711
curtime = time .time ()
653
712
if not os .path .isdir (os .path .dirname (self ._lock_file_path ())):
0 commit comments