diff options
Diffstat (limited to 'raven/utils/basic.py')
-rw-r--r-- | raven/utils/basic.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/raven/utils/basic.py b/raven/utils/basic.py new file mode 100644 index 0000000..4a40e8e --- /dev/null +++ b/raven/utils/basic.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import + +from functools import update_wrapper +import threading + +from raven.utils.compat import iteritems + + +def merge_dicts(*dicts): + out = {} + for d in dicts: + if not d: + continue + + for k, v in iteritems(d): + out[k] = v + return out + + +def varmap(func, var, context=None, name=None): + """ + Executes ``func(key_name, value)`` on all values + recurisively discovering dict and list scoped + values. + """ + if context is None: + context = {} + objid = id(var) + if objid in context: + return func(name, '<...>') + context[objid] = 1 + + if isinstance(var, (list, tuple)): + ret = [varmap(func, f, context, name) for f in var] + else: + ret = func(name, var) + if isinstance(ret, dict): + ret = dict((k, varmap(func, v, context, k)) + for k, v in iteritems(var)) + del context[objid] + return ret + + +class memoize(object): + """ + Memoize the result of a property call. + + >>> class A(object): + >>> @memoize + >>> def func(self): + >>> return 'foo' + """ + + def __init__(self, func): + self.__name__ = func.__name__ + self.__module__ = func.__module__ + self.__doc__ = func.__doc__ + self.func = func + + def __get__(self, obj, type=None): + if obj is None: + return self + d, n = vars(obj), self.__name__ + if n not in d: + d[n] = self.func(obj) + return d[n] + + +def once(func): + """Runs a thing once and once only.""" + lock = threading.Lock() + + def new_func(*args, **kwargs): + if new_func.called: + return + with lock: + if new_func.called: + return + rv = func(*args, **kwargs) + new_func.called = True + return rv + + new_func = update_wrapper(new_func, func) + new_func.called = False + return new_func |