diff options
author | Giampaolo Rodola' <g.rodola@gmail.com> | 2013-12-08 14:36:04 +0100 |
---|---|---|
committer | Giampaolo Rodola' <g.rodola@gmail.com> | 2013-12-08 14:36:04 +0100 |
commit | aaf0f95f076b0f6632492b50455e33e49930d0a5 (patch) | |
tree | c2555f6e442a9cc30d01f9ff4a6c2017f647b8ec /psutil/_compat.py | |
parent | fdeb28aa8e362b7bd48182b589365b575d54b985 (diff) | |
download | psutil-aaf0f95f076b0f6632492b50455e33e49930d0a5.tar.gz |
introduce @lru_cache decorator from python 3.2
Diffstat (limited to 'psutil/_compat.py')
-rw-r--r-- | psutil/_compat.py | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/psutil/_compat.py b/psutil/_compat.py index 5c68f745..9c9684ec 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -272,3 +272,171 @@ except ImportError: getattr(original, attribute).copy()) return fn return inner + + +# py 2.5 functools.update_wrapper +try: + from functools import update_wrapper +except ImportError: + WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') + WRAPPER_UPDATES = ('__dict__',) + + def update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, + updated=WRAPPER_UPDATES): + """Update a wrapper function to look like the wrapped function, see: + http://docs.python.org/library/functools.html#functools.update_wrapper + """ + for attr in assigned: + setattr(wrapper, attr, getattr(wrapped, attr)) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + return wrapper + + +# py 3.2 functools.lru_cache +# Taken from: http://code.activestate.com/recipes/578078 +# Credit: Raymond Hettinger +try: + from functools import lru_cache +except ImportError: + try: + from threading import RLock + except ImportError: + from dummy_threading import RLock + + _CacheInfo = namedtuple("CacheInfo", + ["hits", "misses", "maxsize", "currsize"]) + + class _HashedSeq(list): + __slots__ = 'hashvalue' + + def __init__(self, tup, hash=hash): + self[:] = tup + self.hashvalue = hash(tup) + + def __hash__(self): + return self.hashvalue + + def _make_key(args, kwds, typed, + kwd_mark=(object(), ), + fasttypes=set((int, str, frozenset, type(None))), + sorted=sorted, tuple=tuple, type=type, len=len): + 'Make a cache key from optionally typed positional and keyword arguments' + key = args + if kwds: + sorted_items = sorted(kwds.items()) + key += kwd_mark + for item in sorted_items: + key += item + if typed: + key += tuple(type(v) for v in args) + if kwds: + key += tuple(type(v) for k, v in sorted_items) + elif len(key) == 1 and type(key[0]) in fasttypes: + return key[0] + return _HashedSeq(key) + + def lru_cache(maxsize=100, typed=False): + """Least-recently-used cache decorator, see: + http://docs.python.org/3/library/functools.html#functools.lru_cache + """ + def decorating_function(user_function): + cache = dict() + stats = [0, 0] + HITS, MISSES = 0, 1 + make_key = _make_key + cache_get = cache.get + _len = len + lock = RLock() + root = [] + root[:] = [root, root, None, None] + nonlocal_root = [root] + PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 + if maxsize == 0: + def wrapper(*args, **kwds): + result = user_function(*args, **kwds) + stats[MISSES] += 1 + return result + elif maxsize is None: + def wrapper(*args, **kwds): + key = make_key(args, kwds, typed) + result = cache_get(key, root) + if result is not root: + stats[HITS] += 1 + return result + result = user_function(*args, **kwds) + cache[key] = result + stats[MISSES] += 1 + return result + else: + def wrapper(*args, **kwds): + if kwds or typed: + key = make_key(args, kwds, typed) + else: + key = args + lock.acquire() + try: + link = cache_get(key) + if link is not None: + root, = nonlocal_root + link_prev, link_next, key, result = link + link_prev[NEXT] = link_next + link_next[PREV] = link_prev + last = root[PREV] + last[NEXT] = root[PREV] = link + link[PREV] = last + link[NEXT] = root + stats[HITS] += 1 + return result + finally: + lock.release() + result = user_function(*args, **kwds) + lock.acquire() + try: + root, = nonlocal_root + if key in cache: + pass + elif _len(cache) >= maxsize: + oldroot = root + oldroot[KEY] = key + oldroot[RESULT] = result + root = nonlocal_root[0] = oldroot[NEXT] + oldkey = root[KEY] + root[KEY] = root[RESULT] = None + del cache[oldkey] + cache[key] = oldroot + else: + last = root[PREV] + link = [last, root, key, result] + last[NEXT] = root[PREV] = cache[key] = link + stats[MISSES] += 1 + finally: + lock.release() + return result + + def cache_info(): + """Report cache statistics""" + lock.acquire() + try: + return _CacheInfo(stats[HITS], stats[MISSES], maxsize, + len(cache)) + finally: + lock.release() + + def cache_clear(): + """Clear the cache and cache statistics""" + lock.acquire() + try: + cache.clear() + root = nonlocal_root[0] + root[:] = [root, root, None, None] + stats[:] = [0, 0] + finally: + lock.release() + + wrapper.__wrapped__ = user_function + wrapper.cache_info = cache_info + wrapper.cache_clear = cache_clear + return update_wrapper(wrapper, user_function) + + return decorating_function |