Merge pull request #1385 from davvid/bindir
[git-cola.git] / cola / decorators.py
blobe18c4856b8b61e6efa8821445592c44b445cd79f
1 import errno
2 import functools
5 __all__ = ('decorator', 'memoize', 'interruptable')
8 def decorator(caller, func=None):
9 """
10 Create a new decorator
12 decorator(caller) converts a caller function into a decorator;
13 decorator(caller, func) decorates a function using a caller.
15 """
16 if func is None:
17 # return a decorator
18 @functools.wraps(caller)
19 def _decorator(func, *dummy_args, **dummy_opts):
20 @functools.wraps(func)
21 def _caller(*args, **opts):
22 return caller(func, *args, **opts)
24 return _caller
26 _decorator.func = caller
27 return _decorator
29 # return a decorated function
30 @functools.wraps(func)
31 def _decorated(*args, **opts):
32 return caller(func, *args, **opts)
34 _decorated.func = func
35 return _decorated
38 def memoize(func):
39 """
40 A decorator for memoizing function calls
42 https://en.wikipedia.org/wiki/Memoization
44 """
45 func.cache = {}
46 return decorator(_memoize, func)
49 def _memoize(func, *args, **opts):
50 """Implements memoized cache lookups"""
51 if opts: # frozenset is used to ensure hashability
52 key = (args, frozenset(list(opts.items())))
53 else:
54 key = args
55 cache = func.cache # attribute added by memoize
56 try:
57 result = cache[key]
58 except KeyError:
59 result = cache[key] = func(*args, **opts)
60 return result
63 @decorator
64 def interruptable(func, *args, **opts):
65 """Handle interruptible system calls
67 macOS and others are known to interrupt system calls
69 https://en.wikipedia.org/wiki/PCLSRing
70 http://en.wikipedia.org/wiki/Unix_philosophy#Worse_is_better
72 The @interruptable decorator handles this situation
74 """
75 while True:
76 try:
77 result = func(*args, **opts)
78 except OSError as e:
79 if e.errno in (errno.EINTR, errno.EINVAL):
80 continue
81 raise e
82 break
83 return result