sequenceeditor: keep the Summary column elided
[git-cola.git] / cola / decorators.py
blob9c43b7c3a901781d85a46270d0afae9fbd8e0e9b
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 # pylint: disable=unused-argument
19 @functools.wraps(caller)
20 def _decorator(func, *dummy_args, **dummy_opts):
21 @functools.wraps(func)
22 def _caller(*args, **opts):
23 return caller(func, *args, **opts)
25 return _caller
27 _decorator.func = caller
28 return _decorator
30 # return a decorated function
31 @functools.wraps(func)
32 def _decorated(*args, **opts):
33 return caller(func, *args, **opts)
35 _decorated.func = func
36 return _decorated
39 def memoize(func):
40 """
41 A decorator for memoizing function calls
43 https://en.wikipedia.org/wiki/Memoization
45 """
46 func.cache = {}
47 return decorator(_memoize, func)
50 def _memoize(func, *args, **opts):
51 """Implements memoized cache lookups"""
52 if opts: # frozenset is used to ensure hashability
53 key = (args, frozenset(list(opts.items())))
54 else:
55 key = args
56 cache = func.cache # attribute added by memoize
57 try:
58 result = cache[key]
59 except KeyError:
60 result = cache[key] = func(*args, **opts)
61 return result
64 @decorator
65 def interruptable(func, *args, **opts):
66 """Handle interruptible system calls
68 macOS and others are known to interrupt system calls
70 https://en.wikipedia.org/wiki/PCLSRing
71 http://en.wikipedia.org/wiki/Unix_philosophy#Worse_is_better
73 The @interruptable decorator handles this situation
75 """
76 while True:
77 try:
78 result = func(*args, **opts)
79 except OSError as e:
80 if e.errno in (errno.EINTR, errno.EINVAL):
81 continue
82 raise e
83 break
84 return result