git-cola v1.9.4
[git-cola.git] / cola / decorators.py
blob73587c7b5d252c546ce4a3e67eac1399fec0ee47
1 __all__ = ('decorator', 'memoize', 'interruptable')
3 import errno
6 def decorator(caller, func=None):
7 """
8 Create a new decorator
10 decorator(caller) converts a caller function into a decorator;
11 decorator(caller, func) decorates a function using a caller.
13 """
14 if func is None:
15 # return a decorator
16 def _decorator(f, *args, **opts):
17 def _caller(*args, **opts):
18 return caller(f, *args, **opts)
19 return _caller
20 return _decorator
21 else:
22 # return a decorated function
23 def _decorated(*args, **opts):
24 return caller(func, *args, **opts)
25 return _decorated
28 def memoize(func):
29 """
30 A decorator for memoizing function calls
32 http://en.wikipedia.org/wiki/Memoization
34 """
35 func.cache = {}
36 return decorator(_memoize, func)
39 def _memoize(func, *args, **opts):
40 """Implements memoized cache lookups"""
41 if opts: # frozenset is used to ensure hashability
42 key = args, frozenset(opts.items())
43 else:
44 key = args
45 cache = func.cache # attribute added by memoize
46 try:
47 result = cache[key]
48 except KeyError:
49 result = cache[key] = func(*args, **opts)
50 return result
53 @decorator
54 def interruptable(func, *args, **opts):
55 """Handle interruptable system calls
57 OSX and others are known to interrupt system calls
59 http://en.wikipedia.org/wiki/PCLSRing
60 http://en.wikipedia.org/wiki/Unix_philosophy#Worse_is_better
62 The @interruptable decorator handles this situation
64 """
65 while True:
66 try:
67 result = func(*args, **opts)
68 except IOError, e:
69 if e.errno == errno.EINTR:
70 continue
71 raise e
72 except OSError, e:
73 if e.errno in (errno.EINTR, errno.EINVAL):
74 continue
75 raise e
76 else:
77 break
78 return result