Completed
Push — develop ( 0af8d5...4ae7a2 )
by Jace
01:54
created

yorm.bases.store_after()   B

Complexity

Conditions 6

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6
Metric Value
cc 6
dl 0
loc 22
ccs 13
cts 13
cp 1
crap 6
rs 7.7857

1 Method

Rating   Name   Duplication   Size   Complexity  
B yorm.bases.wrapped() 0 12 5
1
"""Base classes for mapping."""
2
3 1
import abc
4 1
import functools
5
6 1
from .. import common
7 1
from ..mapper import get_mapper
8
9
10 1
log = common.logger(__name__)
11
12 1
TAG = '_modified_by_yorm'
13
14
15 1
def fetch_before(method):
16
    """Decorator for methods that should fetch before call."""
17
18 1
    if getattr(method, TAG, False):
19 1
        return method
20
21 1
    @functools.wraps(method)
22
    def wrapped(self, *args, **kwargs):
23
        """Decorated method."""
24 1
        if not _private_call(method, args):
25 1
            mapper = get_mapper(self)
26 1
            if mapper and mapper.modified:
27 1
                log.debug("Fetching before call: %s", method.__name__)
28 1
                mapper.fetch()
29 1
                if mapper.auto_store:
30 1
                    mapper.store()
31 1
                    mapper.modified = False
32
33 1
        return method(self, *args, **kwargs)
34
35 1
    setattr(wrapped, TAG, True)
36
37 1
    return wrapped
38
39
40 1
def store_after(method):
41
    """Decorator for methods that should store after call."""
42
43 1
    if getattr(method, TAG, False):
44 1
        return method
45
46 1
    @functools.wraps(method)
47
    def wrapped(self, *args, **kwargs):
48
        """Decorated method."""
49 1
        result = method(self, *args, **kwargs)
50
51 1
        if not _private_call(method, args):
52 1
            mapper = get_mapper(self)
53 1
            if mapper and mapper.auto:
54 1
                log.debug("Storing after call: %s", method.__name__)
55 1
                mapper.store()
56
57 1
        return result
58
59 1
    setattr(wrapped, TAG, True)
60
61 1
    return wrapped
62
63
64 1
def _private_call(method, args, prefix='_'):
65
    """Determine if a call's first argument is a private variable name."""
66 1
    if method.__name__ in ('__getattribute__', '__setattr__'):
67 1
        assert isinstance(args[0], str)
68 1
        return args[0].startswith(prefix)
69
    else:
70 1
        return False
71
72
73 1
class Mappable(metaclass=abc.ABCMeta):
74
    """Base class for objects with attributes mapped to file."""
75
76
    # pylint: disable=no-member
77
78 1
    @fetch_before
79
    def __getattribute__(self, name):
80
        """Trigger object update when reading attributes."""
81 1
        return object.__getattribute__(self, name)
82
83 1
    @store_after
84
    def __setattr__(self, name, value):
85
        """Trigger file update when setting attributes."""
86 1
        super().__setattr__(name, value)
87
88 1
    @fetch_before
89
    def __iter__(self):
90
        """Trigger object update when iterating."""
91 1
        return super().__iter__()
92
93 1
    @fetch_before
94
    def __getitem__(self, key):
95
        """Trigger object update when reading an index."""
96 1
        return super().__getitem__(key)
97
98 1
    @store_after
99
    def __setitem__(self, key, value):
100
        """Trigger file update when setting an index."""
101 1
        super().__setitem__(key, value)
102
103 1
    @store_after
104
    def __delitem__(self, key):
105
        """Trigger file update when deleting an index."""
106 1
        super().__delitem__(key)
107
108 1
    @store_after
109
    def append(self, value):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
110
        """Trigger file update when appending items."""
111
        super().append(value)
112