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