widgets: guard against RuntimeError during application shutdown
[git-cola.git] / cola / decorators.py
blobab6dc05fd9163bbb43391d265730710d708b86b9
1 from __future__ import absolute_import, division, print_function, unicode_literals
2 import errno
3 import functools
6 __all__ = ('decorator', 'memoize', 'interruptable')
9 def decorator(caller, func=None):
10 """
11 Create a new decorator
13 decorator(caller) converts a caller function into a decorator;
14 decorator(caller, func) decorates a function using a caller.
16 """
17 if func is None:
18 # return a decorator
19 # pylint: disable=unused-argument
20 @functools.wraps(caller)
21 def _decorator(f, *dummy_args, **dummy_opts):
22 @functools.wraps(f)
23 def _caller(*args, **opts):
24 return caller(f, *args, **opts)
26 return _caller
28 _decorator.func = caller
29 return _decorator
31 # return a decorated function
32 @functools.wraps(func)
33 def _decorated(*args, **opts):
34 return caller(func, *args, **opts)
36 _decorated.func = func
37 return _decorated
40 def memoize(func):
41 """
42 A decorator for memoizing function calls
44 http://en.wikipedia.org/wiki/Memoization
46 """
47 func.cache = {}
48 return decorator(_memoize, func)
51 def _memoize(func, *args, **opts):
52 """Implements memoized cache lookups"""
53 if opts: # frozenset is used to ensure hashability
54 key = (args, frozenset(list(opts.items())))
55 else:
56 key = args
57 cache = func.cache # attribute added by memoize
58 try:
59 result = cache[key]
60 except KeyError:
61 result = cache[key] = func(*args, **opts)
62 return result
65 @decorator
66 def interruptable(func, *args, **opts):
67 """Handle interruptible system calls
69 OSX and others are known to interrupt system calls
71 http://en.wikipedia.org/wiki/PCLSRing
72 http://en.wikipedia.org/wiki/Unix_philosophy#Worse_is_better
74 The @interruptable decorator handles this situation
76 """
77 while True:
78 try:
79 result = func(*args, **opts)
80 except (IOError, OSError) as e:
81 if e.errno in (errno.EINTR, errno.EINVAL):
82 continue
83 raise e
84 else:
85 break
86 return result