Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/virtualenv/util/lock.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
| author | shellac |
|---|---|
| date | Mon, 22 Mar 2021 18:12:50 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4f3585e2f14b |
|---|---|
| 1 """holds locking functionality that works across processes""" | |
| 2 from __future__ import absolute_import, unicode_literals | |
| 3 | |
| 4 import logging | |
| 5 import os | |
| 6 from abc import ABCMeta, abstractmethod | |
| 7 from contextlib import contextmanager | |
| 8 from threading import Lock, RLock | |
| 9 | |
| 10 from filelock import FileLock, Timeout | |
| 11 from six import add_metaclass | |
| 12 | |
| 13 from virtualenv.util.path import Path | |
| 14 | |
| 15 | |
| 16 class _CountedFileLock(FileLock): | |
| 17 def __init__(self, lock_file): | |
| 18 parent = os.path.dirname(lock_file) | |
| 19 if not os.path.isdir(parent): | |
| 20 try: | |
| 21 os.makedirs(parent) | |
| 22 except OSError: | |
| 23 pass | |
| 24 super(_CountedFileLock, self).__init__(lock_file) | |
| 25 self.count = 0 | |
| 26 self.thread_safe = RLock() | |
| 27 | |
| 28 def acquire(self, timeout=None, poll_intervall=0.05): | |
| 29 with self.thread_safe: | |
| 30 if self.count == 0: | |
| 31 super(_CountedFileLock, self).acquire(timeout=timeout, poll_intervall=poll_intervall) | |
| 32 self.count += 1 | |
| 33 | |
| 34 def release(self, force=False): | |
| 35 with self.thread_safe: | |
| 36 if self.count == 1: | |
| 37 super(_CountedFileLock, self).release(force=force) | |
| 38 self.count = max(self.count - 1, 0) | |
| 39 | |
| 40 | |
| 41 _lock_store = {} | |
| 42 _store_lock = Lock() | |
| 43 | |
| 44 | |
| 45 @add_metaclass(ABCMeta) | |
| 46 class PathLockBase(object): | |
| 47 def __init__(self, folder): | |
| 48 path = Path(folder) | |
| 49 self.path = path.resolve() if path.exists() else path | |
| 50 | |
| 51 def __repr__(self): | |
| 52 return "{}({})".format(self.__class__.__name__, self.path) | |
| 53 | |
| 54 def __div__(self, other): | |
| 55 return type(self)(self.path / other) | |
| 56 | |
| 57 def __truediv__(self, other): | |
| 58 return self.__div__(other) | |
| 59 | |
| 60 @abstractmethod | |
| 61 def __enter__(self): | |
| 62 raise NotImplementedError | |
| 63 | |
| 64 @abstractmethod | |
| 65 def __exit__(self, exc_type, exc_val, exc_tb): | |
| 66 raise NotImplementedError | |
| 67 | |
| 68 @abstractmethod | |
| 69 @contextmanager | |
| 70 def lock_for_key(self, name, no_block=False): | |
| 71 raise NotImplementedError | |
| 72 | |
| 73 @abstractmethod | |
| 74 @contextmanager | |
| 75 def non_reentrant_lock_for_key(name): | |
| 76 raise NotImplementedError | |
| 77 | |
| 78 | |
| 79 class ReentrantFileLock(PathLockBase): | |
| 80 def __init__(self, folder): | |
| 81 super(ReentrantFileLock, self).__init__(folder) | |
| 82 self._lock = None | |
| 83 | |
| 84 def _create_lock(self, name=""): | |
| 85 lock_file = str(self.path / "{}.lock".format(name)) | |
| 86 with _store_lock: | |
| 87 if lock_file not in _lock_store: | |
| 88 _lock_store[lock_file] = _CountedFileLock(lock_file) | |
| 89 return _lock_store[lock_file] | |
| 90 | |
| 91 @staticmethod | |
| 92 def _del_lock(lock): | |
| 93 with _store_lock: | |
| 94 if lock is not None: | |
| 95 with lock.thread_safe: | |
| 96 if lock.count == 0: | |
| 97 _lock_store.pop(lock.lock_file, None) | |
| 98 | |
| 99 def __del__(self): | |
| 100 self._del_lock(self._lock) | |
| 101 | |
| 102 def __enter__(self): | |
| 103 self._lock = self._create_lock() | |
| 104 self._lock_file(self._lock) | |
| 105 | |
| 106 def __exit__(self, exc_type, exc_val, exc_tb): | |
| 107 self._release(self._lock) | |
| 108 | |
| 109 def _lock_file(self, lock, no_block=False): | |
| 110 # multiple processes might be trying to get a first lock... so we cannot check if this directory exist without | |
| 111 # a lock, but that lock might then become expensive, and it's not clear where that lock should live. | |
| 112 # Instead here we just ignore if we fail to create the directory. | |
| 113 try: | |
| 114 os.makedirs(str(self.path)) | |
| 115 except OSError: | |
| 116 pass | |
| 117 try: | |
| 118 lock.acquire(0.0001) | |
| 119 except Timeout: | |
| 120 if no_block: | |
| 121 raise | |
| 122 logging.debug("lock file %s present, will block until released", lock.lock_file) | |
| 123 lock.release() # release the acquire try from above | |
| 124 lock.acquire() | |
| 125 | |
| 126 @staticmethod | |
| 127 def _release(lock): | |
| 128 lock.release() | |
| 129 | |
| 130 @contextmanager | |
| 131 def lock_for_key(self, name, no_block=False): | |
| 132 lock = self._create_lock(name) | |
| 133 try: | |
| 134 try: | |
| 135 self._lock_file(lock, no_block) | |
| 136 yield | |
| 137 finally: | |
| 138 self._release(lock) | |
| 139 finally: | |
| 140 self._del_lock(lock) | |
| 141 | |
| 142 @contextmanager | |
| 143 def non_reentrant_lock_for_key(self, name): | |
| 144 with _CountedFileLock(str(self.path / "{}.lock".format(name))): | |
| 145 yield | |
| 146 | |
| 147 | |
| 148 class NoOpFileLock(PathLockBase): | |
| 149 def __enter__(self): | |
| 150 raise NotImplementedError | |
| 151 | |
| 152 def __exit__(self, exc_type, exc_val, exc_tb): | |
| 153 raise NotImplementedError | |
| 154 | |
| 155 @contextmanager | |
| 156 def lock_for_key(self, name, no_block=False): | |
| 157 yield | |
| 158 | |
| 159 @contextmanager | |
| 160 def non_reentrant_lock_for_key(self, name): | |
| 161 yield | |
| 162 | |
| 163 | |
| 164 __all__ = ( | |
| 165 "NoOpFileLock", | |
| 166 "ReentrantFileLock", | |
| 167 "Timeout", | |
| 168 ) |
