| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,7 +57,11 @@ class _TemporaryFileSwap(object): | |||
| 57 | 57 | def __init__(self, file_path): | |
| 58 | 58 | self.file_path = file_path | |
| 59 | 59 | self.tmp_file_path = self.file_path + tempfile.mktemp('','','') | |
| 60 | - os.rename(self.file_path, self.tmp_file_path) | ||
| 60 | + # it may be that the source does not exist | ||
| 61 | + try: | ||
| 62 | + os.rename(self.file_path, self.tmp_file_path) | ||
| 63 | + except OSError: | ||
| 64 | + pass | ||
| 61 | 65 | ||
| 62 | 66 | def __del__(self): | |
| 63 | 67 | if os.path.isfile(self.tmp_file_path): | |
@@ -556,6 +560,7 @@ def from_tree(cls, repo, *treeish, **kwargs): | |||
| 556 | 560 | repo.git.read_tree(*arg_list, **kwargs) | |
| 557 | 561 | index = cls(repo, tmp_index) | |
| 558 | 562 | index.entries # force it to read the file as we will delete the temp-file | |
| 563 | + del(index_handler) # release as soon as possible | ||
| 559 | 564 | finally: | |
| 560 | 565 | if os.path.exists(tmp_index): | |
| 561 | 566 | os.remove(tmp_index) | |
@@ -763,9 +768,6 @@ def write_tree(self, missing_ok=False): | |||
| 763 | 768 | Returns | |
| 764 | 769 | Tree object representing this index | |
| 765 | 770 | """ | |
| 766 | - index_path = self._index_path() | ||
| 767 | - tmp_index_mover = _TemporaryFileSwap(index_path) | ||
| 768 | - | ||
| 769 | 771 | # IMPORTANT: If we have TREE extension data, it will actually | |
| 770 | 772 | # ignore the index and write the stored tree instead. Hence we | |
| 771 | 773 | # temporarily forget about it, and in fact I don't know what git | |
@@ -776,9 +778,14 @@ def write_tree(self, missing_ok=False): | |||
| 776 | 778 | self._extension_data = '' | |
| 777 | 779 | # END extension data special handling | |
| 778 | 780 | ||
| 781 | + index_path = self._index_path() | ||
| 782 | + tmp_index_mover = _TemporaryFileSwap(index_path) | ||
| 783 | + | ||
| 779 | 784 | self.write(index_path) | |
| 780 | 785 | tree_sha = self.repo.git.write_tree(missing_ok=missing_ok) | |
| 781 | 786 | ||
| 787 | + del(tmp_index_mover) # as soon as possible | ||
| 788 | + | ||
| 782 | 789 | if stored_ext_data: | |
| 783 | 790 | self._extension_data = stored_ext_data | |
| 784 | 791 | # END reset stored exstension data | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | 7 | import os | |
| 8 | 8 | import sys | |
| 9 | + import time | ||
| 9 | 10 | import tempfile | |
| 10 | 11 | ||
| 11 | 12 | try: | |
@@ -172,6 +173,45 @@ def _release_lock(self): | |||
| 172 | 173 | os.remove(self._lock_file_path()) | |
| 173 | 174 | self._owns_lock = False | |
| 174 | 175 | ||
| 176 | + class BlockingLockFile(LockFile): | ||
| 177 | + """The lock file will block until a lock could be obtained, or fail after | ||
| 178 | + a specified timeout""" | ||
| 179 | + __slots__ = ("_check_interval", "_max_block_time") | ||
| 180 | + def __init__(self, file_path, check_interval_s=0.3, max_block_time_s=sys.maxint): | ||
| 181 | + """Configure the instance | ||
| 182 | + | ||
| 183 | + ``check_interval_s`` | ||
| 184 | + Period of time to sleep until the lock is checked the next time. | ||
| 185 | + By default, it waits a nearly unlimited time | ||
| 186 | + | ||
| 187 | + ``max_block_time_s`` | ||
| 188 | + Maximum amount of seconds we may lock | ||
| 189 | + """ | ||
| 190 | + super(BlockingLockFile, self).__init__(file_path) | ||
| 191 | + self._check_interval = check_interval_s | ||
| 192 | + self._max_block_time = max_block_time_s | ||
| 193 | + | ||
| 194 | + def _obtain_lock(self): | ||
| 195 | + """This method blocks until it obtained the lock, or raises IOError if | ||
| 196 | + it ran out of time. | ||
| 197 | + If this method returns, you are guranteed to own the lock""" | ||
| 198 | + starttime = time.time() | ||
| 199 | + maxtime = starttime + float(self._max_block_time) | ||
| 200 | + while True: | ||
| 201 | + try: | ||
| 202 | + self._obtain_lock_or_raise() | ||
| 203 | + except IOError: | ||
| 204 | + curtime = time.time() | ||
| 205 | + if curtime >= maxtime: | ||
| 206 | + msg = "Waited %f seconds for lock at %r" % ( maxtime - starttime, self._lock_file_path()) | ||
| 207 | + raise IOError(msg) | ||
| 208 | + # END abort if we wait too long | ||
| 209 | + time.sleep(self._check_interval) | ||
| 210 | + else: | ||
| 211 | + break | ||
| 212 | + # END endless loop | ||
| 213 | + | ||
| 214 | + | ||
| 175 | 215 | ||
| 176 | 216 | class ConcurrentWriteOperation(LockFile): | |
| 177 | 217 | """ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,6 +11,7 @@ | |||
| 11 | 11 | from git.utils import * | |
| 12 | 12 | from git import * | |
| 13 | 13 | from git.cmd import dashify | |
| 14 | + import time | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | class TestUtils(TestCase): | |
@@ -53,6 +54,19 @@ def test_lock_file(self): | |||
| 53 | 54 | lock_file._obtain_lock_or_raise() | |
| 54 | 55 | lock_file._release_lock() | |
| 55 | 56 | ||
| 57 | + def test_blocking_lock_file(self): | ||
| 58 | + my_file = tempfile.mktemp() | ||
| 59 | + lock_file = BlockingLockFile(my_file) | ||
| 60 | + lock_file._obtain_lock() | ||
| 61 | + | ||
| 62 | + # next one waits for the lock | ||
| 63 | + start = time.time() | ||
| 64 | + wait_time = 0.1 | ||
| 65 | + wait_lock = BlockingLockFile(my_file, 0.05, wait_time) | ||
| 66 | + self.failUnlessRaises(IOError, wait_lock._obtain_lock) | ||
| 67 | + elapsed = time.time() - start | ||
| 68 | + assert elapsed <= wait_time + 0.02 # some extra time it may cost | ||
| 69 | + | ||
| 56 | 70 | def _cmp_contents(self, file_path, data): | |
| 57 | 71 | # raise if data from file at file_path | |
| 58 | 72 | # does not match data string | |
| Back | FazBrowse Home | New Git URL |
0 commit comments