| | """Function decorator helpers.""" |
| |
|
| | __all__ = () |
| |
|
| | import functools |
| |
|
| | |
| | |
| | |
| | |
| |
|
| |
|
| | def _condition_info(func, cache, key, lock, cond, info): |
| | hits = misses = 0 |
| | pending = set() |
| |
|
| | def wrapper(*args, **kwargs): |
| | nonlocal hits, misses |
| | k = key(*args, **kwargs) |
| | with lock: |
| | cond.wait_for(lambda: k not in pending) |
| | try: |
| | result = cache[k] |
| | hits += 1 |
| | return result |
| | except KeyError: |
| | pending.add(k) |
| | misses += 1 |
| | try: |
| | v = func(*args, **kwargs) |
| | with lock: |
| | try: |
| | cache[k] = v |
| | except ValueError: |
| | pass |
| | return v |
| | finally: |
| | with lock: |
| | pending.remove(k) |
| | cond.notify_all() |
| |
|
| | def cache_clear(): |
| | nonlocal hits, misses |
| | with lock: |
| | cache.clear() |
| | hits = misses = 0 |
| |
|
| | def cache_info(): |
| | with lock: |
| | return info(hits, misses) |
| |
|
| | wrapper.cache_clear = cache_clear |
| | wrapper.cache_info = cache_info |
| | return wrapper |
| |
|
| |
|
| | def _locked_info(func, cache, key, lock, info): |
| | hits = misses = 0 |
| |
|
| | def wrapper(*args, **kwargs): |
| | nonlocal hits, misses |
| | k = key(*args, **kwargs) |
| | with lock: |
| | try: |
| | result = cache[k] |
| | hits += 1 |
| | return result |
| | except KeyError: |
| | misses += 1 |
| | v = func(*args, **kwargs) |
| | with lock: |
| | try: |
| | |
| | |
| | |
| | return cache.setdefault(k, v) |
| | except ValueError: |
| | return v |
| |
|
| | def cache_clear(): |
| | nonlocal hits, misses |
| | with lock: |
| | cache.clear() |
| | hits = misses = 0 |
| |
|
| | def cache_info(): |
| | with lock: |
| | return info(hits, misses) |
| |
|
| | wrapper.cache_clear = cache_clear |
| | wrapper.cache_info = cache_info |
| | return wrapper |
| |
|
| |
|
| | def _unlocked_info(func, cache, key, info): |
| | hits = misses = 0 |
| |
|
| | def wrapper(*args, **kwargs): |
| | nonlocal hits, misses |
| | k = key(*args, **kwargs) |
| | try: |
| | result = cache[k] |
| | hits += 1 |
| | return result |
| | except KeyError: |
| | misses += 1 |
| | v = func(*args, **kwargs) |
| | try: |
| | cache[k] = v |
| | except ValueError: |
| | pass |
| | return v |
| |
|
| | def cache_clear(): |
| | nonlocal hits, misses |
| | cache.clear() |
| | hits = misses = 0 |
| |
|
| | def cache_info(): |
| | return info(hits, misses) |
| |
|
| | wrapper.cache_clear = cache_clear |
| | wrapper.cache_info = cache_info |
| | return wrapper |
| |
|
| |
|
| | def _uncached_info(func, info): |
| | misses = 0 |
| |
|
| | def wrapper(*args, **kwargs): |
| | nonlocal misses |
| | misses += 1 |
| | return func(*args, **kwargs) |
| |
|
| | def cache_clear(): |
| | nonlocal misses |
| | misses = 0 |
| |
|
| | wrapper.cache_clear = cache_clear |
| | wrapper.cache_info = lambda: info(0, misses) |
| | return wrapper |
| |
|
| |
|
| | def _condition(func, cache, key, lock, cond): |
| | pending = set() |
| |
|
| | def wrapper(*args, **kwargs): |
| | k = key(*args, **kwargs) |
| | with lock: |
| | cond.wait_for(lambda: k not in pending) |
| | try: |
| | result = cache[k] |
| | return result |
| | except KeyError: |
| | pending.add(k) |
| | try: |
| | v = func(*args, **kwargs) |
| | with lock: |
| | try: |
| | cache[k] = v |
| | except ValueError: |
| | pass |
| | return v |
| | finally: |
| | with lock: |
| | pending.remove(k) |
| | cond.notify_all() |
| |
|
| | def cache_clear(): |
| | with lock: |
| | cache.clear() |
| |
|
| | wrapper.cache_clear = cache_clear |
| | return wrapper |
| |
|
| |
|
| | def _locked(func, cache, key, lock): |
| | def wrapper(*args, **kwargs): |
| | k = key(*args, **kwargs) |
| | with lock: |
| | try: |
| | return cache[k] |
| | except KeyError: |
| | pass |
| | v = func(*args, **kwargs) |
| | with lock: |
| | try: |
| | |
| | |
| | |
| | return cache.setdefault(k, v) |
| | except ValueError: |
| | return v |
| |
|
| | def cache_clear(): |
| | with lock: |
| | cache.clear() |
| |
|
| | wrapper.cache_clear = cache_clear |
| | return wrapper |
| |
|
| |
|
| | def _unlocked(func, cache, key): |
| | def wrapper(*args, **kwargs): |
| | k = key(*args, **kwargs) |
| | try: |
| | return cache[k] |
| | except KeyError: |
| | pass |
| | v = func(*args, **kwargs) |
| | try: |
| | cache[k] = v |
| | except ValueError: |
| | pass |
| | return v |
| |
|
| | wrapper.cache_clear = lambda: cache.clear() |
| | return wrapper |
| |
|
| |
|
| | def _uncached(func): |
| | def wrapper(*args, **kwargs): |
| | return func(*args, **kwargs) |
| |
|
| | wrapper.cache_clear = lambda: None |
| | return wrapper |
| |
|
| |
|
| | def _wrapper(func, cache, key, lock=None, cond=None, info=None): |
| | if info is not None: |
| | if cache is None: |
| | wrapper = _uncached_info(func, info) |
| | elif cond is not None and lock is not None: |
| | wrapper = _condition_info(func, cache, key, lock, cond, info) |
| | elif cond is not None: |
| | wrapper = _condition_info(func, cache, key, cond, cond, info) |
| | elif lock is not None: |
| | wrapper = _locked_info(func, cache, key, lock, info) |
| | else: |
| | wrapper = _unlocked_info(func, cache, key, info) |
| | else: |
| | if cache is None: |
| | wrapper = _uncached(func) |
| | elif cond is not None and lock is not None: |
| | wrapper = _condition(func, cache, key, lock, cond) |
| | elif cond is not None: |
| | wrapper = _condition(func, cache, key, cond, cond) |
| | elif lock is not None: |
| | wrapper = _locked(func, cache, key, lock) |
| | else: |
| | wrapper = _unlocked(func, cache, key) |
| | wrapper.cache_info = None |
| |
|
| | wrapper.cache = cache |
| | wrapper.cache_key = key |
| | wrapper.cache_lock = lock if lock is not None else cond |
| | wrapper.cache_condition = cond |
| |
|
| | return functools.update_wrapper(wrapper, func) |
| |
|