File: decorators.py

package info (click to toggle)
glueviz 0.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 17,180 kB
  • ctags: 6,728
  • sloc: python: 37,111; makefile: 134; sh: 60
file content (87 lines) | stat: -rw-r--r-- 2,231 bytes parent folder | download | duplicates (2)
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
84
85
86
87
from __future__ import absolute_import, division, print_function

from functools import wraps


__all__ = ['memoize', 'singleton', 'memoize_attr_check']


def _make_key(args, kwargs):
    return args, frozenset(kwargs.items())


def memoize(func):
    """Save results of function calls to avoid repeated calculation"""
    memo = {}

    @wraps(func)
    def wrapper(*args, **kwargs):

        # Note that here we have two separate try...except statements, because
        # we want to make sure that we catch only TypeError on the first
        # statement, and both TypeError and KeyError on the second.

        try:
            key = _make_key(args, kwargs)
        except TypeError:  # unhashable input
            return func(*args, **kwargs)

        try:
            return memo[key]
        except KeyError:
            result = func(*args, **kwargs)
            memo[key] = result
            return result
        except TypeError:  # unhashable input
            return func(*args, **kwargs)

    wrapper.__memoize_cache = memo
    return wrapper


def clear_cache(func):
    """
    Clear the cache of a function that has potentially been
    decorated by memoize. Safely ignores non-decorated functions
    """
    try:
        func.__memoize_cache.clear()
    except AttributeError:
        pass


def memoize_attr_check(attr):
    """ Memoize a method call, cached both on arguments and given attribute
    of first argument (which is presumably self)

    Has the effect of re-calculating results if a specific attribute changes
    """

    def decorator(func):
        # must return a decorator function

        @wraps(func)
        def result(*args, **kwargs):
            first_arg = getattr(args[0], attr)
            return memo(first_arg, *args, **kwargs)

        @memoize
        def memo(*args, **kwargs):
            return func(*args[1:], **kwargs)

        return result

    return decorator


def singleton(cls):
    """Turn a class into a singleton, such that new objects
    in this class share the same instance"""
    instances = {}

    @wraps(cls)
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance