Completed
Push — develop ( 680e07...e5c8fa )
by Jace
02:57
created

Mappable.popitem()   A

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

Importance

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