git-cola v2.11
[git-cola.git] / cola / decorators.py
blob8efd05d6bfb1b6c23f6368a28611aed5baf23c2b
1 from __future__ import division, absolute_import, 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 @functools.wraps(caller)
20 def _decorator(f, *args, **opts):
21 @functools.wraps(f)
22 def _caller(*args, **opts):
23 return caller(f, *args, **opts)
24 return _caller
25 _decorator.func = caller
26 return _decorator
27 else:
28 # return a decorated function
29 @functools.wraps(func)
30 def _decorated(*args, **opts):
31 return caller(func, *args, **opts)
32 _decorated.func = func
33 return _decorated
36 def memoize(func):
37 """
38 A decorator for memoizing function calls
40 http://en.wikipedia.org/wiki/Memoization
42 """
43 func.cache = {}
44 return decorator(_memoize, func)
47 def _memoize(func, *args, **opts):
48 """Implements memoized cache lookups"""
49 if opts: # frozenset is used to ensure hashability
50 key = args, frozenset(opts.items())
51 else:
52 key = args
53 cache = func.cache # attribute added by memoize
54 try:
55 result = cache[key]
56 except KeyError:
57 result = cache[key] = func(*args, **opts)
58 return result
61 @decorator
62 def interruptable(func, *args, **opts):
63 """Handle interruptable system calls
65 OSX and others are known to interrupt system calls
67 http://en.wikipedia.org/wiki/PCLSRing
68 http://en.wikipedia.org/wiki/Unix_philosophy#Worse_is_better
70 The @interruptable decorator handles this situation
72 """
73 while True:
74 try:
75 result = func(*args, **opts)
76 except IOError as e:
77 if e.errno == errno.EINTR:
78 continue
79 raise e
80 except OSError as e:
81 if e.errno in (errno.EINTR, errno.EINVAL):
82 continue
83 raise e
84 else:
85 break
86 return result