Completed
Pull Request — master (#179)
by Björn
02:26
created

neovim.api.ApiProperty.__set__()   A

Complexity

Conditions 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625
Metric Value
cc 2
dl 0
loc 4
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
1
"""Code shared between the API classes."""
2 6
import functools
3
4
5 6
class Remote(object):
6
7
    """Base class for Nvim objects(buffer/window/tabpage).
8
9
    Each type of object has it's own specialized class with API wrappers around
10
    the msgpack-rpc session. This implements equality which takes the remote
11
    object handle into consideration.
12
    """
13
14 6
    def __eq__(self, other):
15
        """Return True if `self` and `other` are the same object."""
16 6
        return (hasattr(other, 'code_data') and
17
                other.code_data == self.code_data)
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named code_data.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
18
19 6
    def __hash__(self):
20
        """Return hash based on remote object id."""
21 6
        return self.code_data.__hash__()
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named code_data.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
22
23 6
    def request(self, name, *args, **kwargs):
24
        """Wrapper for nvim.request."""
25 6
        return self._session.request(name, self, *args, **kwargs)
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _session.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
26
27
28 6
class RemoteApi(object):
29 6
    def __init__(self, session, prefix, args=()):
30 6
        self._session = session
31 6
        self._prefix = prefix
32 6
        self._args = args
33
34 6
    def __getattr__(self, name):
35
        return functools.partial(self._session.request,self._prefix + name, *self._args)
36
37 6
class ApiMethod(object):
38
    """A data descriptor that sets and returns values
39
       normally and prints a message logging their access.
40
    """
41
42 6
    def __init__(self, name):
43 6
        self.name = name
44
45 6
    def __get__(self, obj, objtype):
46 6
        return functools.partial(obj.request, self.name)
47
48 6
class ApiProperty(object):
49
    """A data descriptor that sets and returns values
50
       normally and prints a message logging their access.
51
    """
52
53 6
    def __init__(self, getter, setter=None):
54 6
        self._getter = getter
55 6
        self._setter = setter
56
57 6
    def __get__(self, obj, objtype):
58 6
        return obj.request(self._getter)
59
60 6
    def __set__(self, obj, val):
61 6
        if self._setter is None:
62
            raise AttributeError
63 6
        obj.request(self._setter, val)
64
65 6
class RemoteMap(object):
66
67
    """Represents a string->object map stored in Nvim.
68
69
    This is the dict counterpart to the `RemoteSequence` class, but it is used
70
    as a generic way of retrieving values from the various map-like data
71
    structures present in Nvim.
72
73
    It is used to provide a dict-like API to vim variables and options.
74
    """
75
76 6
    def __init__(self, session, get_method, set_method, self_obj=None):
77
        """Initialize a RemoteMap with session, getter/setter and self_obj."""
78 6
        self._get = _wrap(session, get_method, self_obj)
79 6
        self._set = None
80 6
        if set_method:
81 6
            self._set = _wrap(session, set_method, self_obj)
82
83 6
    def __getitem__(self, key):
84
        """Return a map value by key."""
85 6
        return self._get(key)
86
87 6
    def __setitem__(self, key, value):
88
        """Set a map value by key(if the setter was provided)."""
89 6
        if not self._set:
90
            raise TypeError('This dict is read-only')
91 6
        self._set(key, value)
92
93 6
    def __delitem__(self, key):
94
        """Delete a map value by associating None with the key."""
95
        if not self._set:
96
            raise TypeError('This dict is read-only')
97
        return self._set(key, None)
98
99 6
    def __contains__(self, key):
100
        """Check if key is present in the map."""
101
        try:
102
            self._get(key)
103
            return True
104
        except Exception:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
105
            return False
106
107 6
    def get(self, key, default=None):
108
        """Return value for key if present, else a default value."""
109
        try:
110
            return self._get(key)
111
        except Exception:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
112
            return default
113
114
115 6
class RemoteSequence(object):
116
117
    """Represents a sequence of objects stored in Nvim.
118
119
    This class is used to wrap msgapck-rpc functions that work on Nvim
120
    sequences(of lines, buffers, windows and tabpages) with an API that
121
    is similar to the one provided by the python-vim interface.
122
123
    For example, the 'buffers' property of the `Nvim class is a RemoteSequence
124
    sequence instance, and the expression `nvim.buffers[0]` is translated to
125
    session.request('vim_get_buffers')[0].
126
127
    It can also receive an optional self_obj that will be passed as first
128
    argument of the request. For example, `tabpage.windows[0]` is translated
129
    to: session.request('tabpage_get_windows', tabpage_instance)[0].
130
131
    One important detail about this class is that all methods will fetch the
132
    sequence into a list and perform the necessary manipulation
133
    locally(iteration, indexing, counting, etc).
134
    """
135
136 6
    def __init__(self, session, method, self_obj=None):
137
        """Initialize a RemoteSequence with session, method and self_obj."""
138 6
        self._fetch = _wrap(session, method, self_obj)
139
140 6
    def __len__(self):
141
        """Return the length of the remote sequence."""
142 6
        return len(self._fetch())
143
144 6
    def __getitem__(self, idx):
145
        """Return a sequence item by index."""
146 6
        if not isinstance(idx, slice):
147 6
            return self._fetch()[idx]
148 1
        return self._fetch()[idx.start:idx.stop]
149
150 6
    def __iter__(self):
151
        """Return an iterator for the sequence."""
152 6
        items = self._fetch()
153 6
        for item in items:
154 6
            yield item
155
156 6
    def __contains__(self, item):
157
        """Check if an item is present in the sequence."""
158 6
        return item in self._fetch()
159
160
161 6
def _identity(obj, session, method, kind):
0 ignored issues
show
Unused Code introduced by
The argument session seems to be unused.
Loading history...
Unused Code introduced by
The argument method seems to be unused.
Loading history...
Unused Code introduced by
The argument kind seems to be unused.
Loading history...
162
    return obj
163
164
165
166 6
class DecodeHook(object):
167
168
    """SessionHook subclass that decodes utf-8 strings coming from Nvim.
169
170
    This class is useful for python3, where strings are now unicode by
171
    default(byte strings need to be prefixed with "b").
172
    """
173
174 6
    def __init__(self, encoding='utf-8', encoding_errors='strict'):
175
        """Initialize with encoding and encoding errors policy."""
176 3
        self.encoding = encoding
177 3
        self.encoding_errors = encoding_errors
178
179 6
    def decode_if_bytes(self, obj):
180 3
        if isinstance(obj, bytes):
181 3
            return obj.decode(self.encoding, errors=self.encoding_errors)
182 3
        return obj
183
184 6
    def walk(self, obj):
185
        """Decode bytes found in obj (any msgpack object).
186
187
        Uses encoding and policy specified in constructor.
188
        """
189 3
        return walk(self.decode_if_bytes, obj)
190
191 6
def walk(fn, obj, *args):
192
    """Recursively walk an object graph applying `fn`/`args` to objects."""
193 6
    if type(obj) in [list, tuple]:
194 6
        return list(walk(fn, o, *args) for o in obj)
195 6
    if type(obj) is dict:
196 6
        return dict((walk(fn, k, *args), walk(fn, v, *args)) for k, v in
197
                    obj.items())
198 6
    return fn(obj, *args)
199
200
201 6
def _wrap(session, method, self_obj):
202 6
    if self_obj is not None:
203 6
        return lambda *args: session.request(method, self_obj, *args)
204
    else:
205
        return lambda *args: session.request(method, *args)
206