Completed
Push — develop ( fa46d4...596e0b )
by Jace
9s
created

Mappable.__delitem__()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
cc 1
crap 1
1
"""Base classes for mapping."""
2
3 1
import abc
4 1
import functools
5 1
import logging
6
7 1
from .. import common
8
9 1
log = logging.getLogger(__name__)
10
11
12 1 View Code Duplication
def load_before(method):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
13
    """Decorator for methods that should load before call."""
14
15 1
    if getattr(method, '_load_before', False):
16 1
        return method
17
18 1
    @functools.wraps(method)
19
    def wrapped(self, *args, **kwargs):
20
        """Decorated method."""
21 1
        if not _private_call(method, args):
22 1
            mapper = common.get_mapper(self)
23 1
            if mapper and mapper.modified:
24 1
                log.debug("Loading before call: %s", method.__name__)
25 1
                mapper.load()
26 1
                if mapper.auto_save_after_load:
27 1
                    mapper.save()
28 1
                    mapper.modified = False
29
30 1
        return method(self, *args, **kwargs)
31
32 1
    setattr(wrapped, '_load_before', True)
33
34 1
    return wrapped
35
36
37 1 View Code Duplication
def save_after(method):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
38
    """Decorator for methods that should save after call."""
39
40 1
    if getattr(method, '_save_after', False):
41 1
        return method
42
43 1
    @functools.wraps(method)
44
    def wrapped(self, *args, **kwargs):
45
        """Decorated method."""
46 1
        result = method(self, *args, **kwargs)
47
48 1
        if not _private_call(method, args):
49 1
            mapper = common.get_mapper(self)
50 1
            if mapper and mapper.auto_save:
51 1
                log.debug("Saving after call: %s", method.__name__)
52 1
                mapper.save()
53
54 1
        return result
55
56 1
    setattr(wrapped, '_save_after', True)
57
58 1
    return wrapped
59
60
61 1
def _private_call(method, args, prefix='_'):
62
    """Determine if a call's first argument is a private variable name."""
63 1
    if method.__name__ in ('__getattribute__', '__setattr__'):
64 1
        assert isinstance(args[0], str)
65 1
        return args[0].startswith(prefix)
66
    else:
67 1
        return False
68
69
70 1
class Mappable(metaclass=abc.ABCMeta):
71
    """Base class for objects with attributes mapped to file."""
72
73
    # pylint: disable=no-member
74
75 1
    @load_before
76
    def __getattribute__(self, name):
77 1
        return object.__getattribute__(self, name)
78
79 1
    @save_after
80
    def __setattr__(self, name, value):
81 1
        super().__setattr__(name, value)
82
83 1
    @load_before
84
    def __iter__(self):
85 1
        return super().__iter__()
86
87 1
    @load_before
88
    def __getitem__(self, key):
89 1
        return super().__getitem__(key)
90
91 1
    @save_after
92
    def __setitem__(self, key, value):
93 1
        super().__setitem__(key, value)
94
95 1
    @save_after
96
    def __delitem__(self, key):
97 1
        super().__delitem__(key)
98
99 1
    @save_after
100
    def append(self, *args, **kwargs):
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...
101 1
        super().append(*args, **kwargs)
102
103 1
    @save_after
104
    def extend(self, *args, **kwargs):
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...
105
        super().extend(*args, **kwargs)
106
107 1
    @save_after
108
    def insert(self, *args, **kwargs):
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...
109 1
        super().insert(*args, **kwargs)
110
111 1
    @save_after
112
    def remove(self, *args, **kwargs):
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...
113
        super().remove(*args, **kwargs)
114
115 1
    @save_after
116
    def pop(self, *args, **kwargs):
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...
117
        super().pop(*args, **kwargs)
118
119 1
    @save_after
120
    def clear(self, *args, **kwargs):
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...
121 1
        super().clear(*args, **kwargs)
122
123 1
    @save_after
124
    def sort(self, *args, **kwargs):
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...
125
        super().sort(*args, **kwargs)
126
127 1
    @save_after
128
    def reverse(self, *args, **kwargs):
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...
129
        super().reverse(*args, **kwargs)
130
131 1
    @save_after
132
    def popitem(self, *args, **kwargs):
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...
133
        super().popitem(*args, **kwargs)
134
135 1
    @save_after
136
    def update(self, *args, **kwargs):
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...
137 1
        super().update(*args, **kwargs)
138
139
140 1
_LOAD_BEFORE_METHODS = [
141
    '__getattribute__',
142
    '__iter__',
143
    '__getitem__',
144
]
145 1
_SAVE_AFTER_METHODS = [
146
    '__setattr__',
147
    '__setitem__',
148
    '__delitem__',
149
    'append',
150
    'extend',
151
    'insert',
152
    'remove',
153
    'pop',
154
    'clear',
155
    'sort',
156
    'reverse',
157
    'popitem',
158
    'update',
159
]
160
161
162 1
def patch_methods(instance):
163 1
    log.debug("Patching methods on: %r", instance)
164 1
    cls = instance.__class__
165
166 1
    for name in _LOAD_BEFORE_METHODS:
167 1
        try:
168 1
            method = getattr(cls, name)
169 1
        except AttributeError:
170 1
            log.trace("No method: %s", name)
171
        else:
172 1
            modified_method = load_before(method)
173 1
            setattr(cls, name, modified_method)
174 1
            log.trace("Patched to load before call: %s", name)
175
176 1
    for name in _SAVE_AFTER_METHODS:
177 1
        try:
178 1
            method = getattr(cls, name)
179 1
        except AttributeError:
180 1
            log.trace("No method: %s", name)
181
        else:
182 1
            modified_method = save_after(method)
183 1
            setattr(cls, name, modified_method)
184
            log.trace("Patched to save after call: %s", name)
185