summaryrefslogtreecommitdiff
path: root/raven/utils/basic.py
diff options
context:
space:
mode:
Diffstat (limited to 'raven/utils/basic.py')
-rw-r--r--raven/utils/basic.py85
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