Skip to content

utils

classUtils

cached_class(klass)

Decorator to cache class instances by constructor arguments. This results in a class that behaves like a singleton for each set of constructor arguments, ensuring efficiency.

Note that this should be used for immutable classes only. Having a cached mutable class makes very little sense. For efficiency, avoid using this decorator for situations where there are many constructor arguments permutations.

The keywords argument dictionary is converted to a tuple because dicts are mutable; keywords themselves are strings and so are always hashable, but if any arguments (keyword or positional) are non- hashable, that set of arguments is not cached.

Source code in src/easyscience/utils/classUtils.py
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
88
89
def cached_class(klass):
    """Decorator to cache class instances by constructor arguments. This
    results in a class that behaves like a singleton for each set of
    constructor arguments, ensuring efficiency.

    Note that this should be used for *immutable classes only*.  Having
    a cached mutable class makes very little sense.  For efficiency,
    avoid using this decorator for situations where there are many
    constructor arguments permutations.

    The keywords argument dictionary is converted to a tuple because
    dicts are mutable; keywords themselves are strings and so are always
    hashable, but if any arguments (keyword or positional) are non-
    hashable, that set of arguments is not cached.
    """
    cache = {}

    @wraps(klass, assigned=('__name__', '__module__'), updated=())
    class _decorated(klass):
        # The wraps decorator can't do this because __doc__
        # isn't writable once the class is created
        __doc__ = klass.__doc__

        def __new__(cls, *args, **kwargs):
            """Pass through...

            :param args:
            :param kwargs:
            :return:
            """
            key = (cls,) + args + tuple(kwargs.items())
            try:
                inst = cache.get(key, None)
            except TypeError:
                # Can't cache this set of arguments
                inst = key = None
            if inst is None:
                # Technically this is cheating, but it works,
                # and takes care of initializing the instance
                # (so we can override __init__ below safely);
                # calling up to klass.__new__ would be the
                # "official" way to create the instance, but
                # that raises DeprecationWarning if there are
                # args or kwargs and klass does not override
                # __new__ (which most classes don't), because
                # object.__new__ takes no parameters (and in
                # Python 3 the warning will become an error)
                inst = klass(*args, **kwargs)
                # This makes isinstance and issubclass work
                # properly
                inst.__class__ = cls
                if key is not None:
                    cache[key] = inst
            return inst

        def __init__(self, *args, **kwargs):
            # This will be called every time __new__ is
            # called, so we skip initializing here and do
            # it only when the instance is created above
            pass

    return _decorated

singleton(cls)

This decorator can be used to create a singleton out of a class.

Usage::

@singleton
class MySingleton:
    def __init__():
        pass
Source code in src/easyscience/utils/classUtils.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def singleton(cls):
    """This decorator can be used to create a singleton out of a class.

    Usage::

        @singleton
        class MySingleton:
            def __init__():
                pass
    """

    instances = {}

    def get_instance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]

    return get_instance

decorators

counted(func)

Counts how many times a function has been called and adds a func.calls to it's properties :param func: Function to be counted :return: Results from function call.

Source code in src/easyscience/utils/decorators.py
44
45
46
47
48
49
50
51
52
53
54
55
56
def counted(func):
    """Counts how many times a function has been called and adds a
    `func.calls` to it's properties :param func: Function to be counted
    :return: Results from function call.
    """

    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        wrapped.n_calls += 1
        return func(*args, **kwargs)

    wrapped.n_calls = 0
    return wrapped

deprecated(func)

This is a decorator which can be used to mark functions as deprecated.

It will result in a warning being emitted when the function is used.

Source code in src/easyscience/utils/decorators.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def deprecated(func):
    """This is a decorator which can be used to mark functions as
    deprecated.

    It will result in a warning being emitted when the function is used.
    """

    @functools.wraps(func)
    def new_func(*args, **kwargs):
        warnings.warn_explicit(
            'Call to deprecated function {}.'.format(func.__name__),
            category=DeprecationWarning,
            filename=func.__code__.co_filename,
            lineno=func.__code__.co_firstlineno + 1,
        )
        return func(*args, **kwargs)

    return new_func

memoized

Decorator.

Caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned (not reevaluated).

Source code in src/easyscience/utils/decorators.py
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
class memoized:
    """Decorator.

    Caches a function's return value each time it is called. If called
    later with the same arguments, the cached value is returned (not
    reevaluated).
    """

    def __init__(self, func):
        self.func = func
        self.cache = {}

    def __call__(self, *args):
        if not isinstance(args, collections.abc.Hashable):
            # uncacheable. a list, for instance.
            # better to not cache than blow up.
            return self.func(*args)
        if args in self.cache:
            return self.cache[args]
        value = self.func(*args)
        self.cache[args] = value
        return value

    def __repr__(self) -> str:
        """Return the function's docstring."""
        return self.func.__doc__

    def __get__(self, obj, objtype):
        """Support instance methods."""
        return functools.partial(self.__call__, obj)

__get__(obj, objtype)

Support instance methods.

Source code in src/easyscience/utils/decorators.py
39
40
41
def __get__(self, obj, objtype):
    """Support instance methods."""
    return functools.partial(self.__call__, obj)

__repr__()

Return the function's docstring.

Source code in src/easyscience/utils/decorators.py
35
36
37
def __repr__(self) -> str:
    """Return the function's docstring."""
    return self.func.__doc__

time_it(func)

Times a function and reports the time either to the class' log or the base logger :param func: function to be timed :return: callable function with timer.

Source code in src/easyscience/utils/decorators.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def time_it(func):
    """Times a function and reports the time either to the class' log or
    the base logger :param func: function to be timed :return: callable
    function with timer.
    """
    name = func.__module__ + '.' + func.__name__
    time_logger = global_object.log.getLogger('timer.' + name)

    @functools.wraps(func)
    def _time_it(*args, **kwargs):
        start = int(round(time() * 1000))
        try:
            return func(*args, **kwargs)
        finally:
            end_ = int(round(time() * 1000)) - start
            time_logger.debug(f'\033[1;34;49mExecution time: {end_ if end_ > 0 else 0} ms\033[0m')

    return _time_it

string

transformation_to_string(matrix, translation_vec=(0, 0, 0), components=('x', 'y', 'z'), c='', delim=',')

Convenience method.

Given matrix returns string, e.g. x+2y+1/4 :param matrix : param translation_vec :param components: either ('x', 'y', 'z') or ('a', 'b', 'c') :param c: optional additional character to print (used for magmoms) :param delim: delimiter :return: xyz string

Source code in src/easyscience/utils/string.py
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
def transformation_to_string(
    matrix, translation_vec=(0, 0, 0), components=('x', 'y', 'z'), c='', delim=','
):
    """Convenience method.

    Given matrix returns string, e.g. x+2y+1/4
    :param matrix : param translation_vec
    :param components: either ('x', 'y', 'z') or ('a', 'b', 'c')
    :param c: optional additional character to print (used for magmoms)
    :param delim: delimiter
    :return: xyz string
    """
    parts = []
    for i in range(3):
        s = ''
        m = matrix[i]
        t = translation_vec[i]
        for j, dim in enumerate(components):
            if m[j] != 0:
                f = Fraction(m[j]).limit_denominator()
                if s != '' and f >= 0:
                    s += '+'
                if abs(f.numerator) != 1:
                    s += str(f.numerator)
                elif f < 0:
                    s += '-'
                s += c + dim
                if f.denominator != 1:
                    s += '/' + str(f.denominator)
        if t != 0:
            s += ('+' if (t > 0 and s != '') else '') + str(Fraction(t).limit_denominator())
        if s == '':
            s += '0'
        parts.append(s)
    return delim.join(parts)