Completed
Pull Request — master (#179)
by Björn
62:29 queued 39:48
created

neovim.api.SessionHook   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 39
Duplicated Lines 0 %

Test Coverage

Coverage 91.67%
Metric Value
dl 0
loc 39
ccs 11
cts 12
cp 0.9167
rs 10
wmc 6

4 Methods

Rating   Name   Duplication   Size   Complexity  
A neovim.api.DecodeHook.__init__() 0 4 1
A neovim.api._identity() 0 2 1
A neovim.api.DecodeHook.decode_if_bytes() 0 4 2
A neovim.api.DecodeHook.walk() 0 6 1
1
"""Code shared between the API classes."""
2
import functools
3
4 6
5
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 6
14
    def __eq__(self, other):
15 6
        """Return True if `self` and `other` are the same object."""
16
        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 6
19
    def __hash__(self):
20 6
        """Return hash based on remote object id."""
21
        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
class RemoteApi(object):
24
    def __init__(self, session, prefix, args=()):
25
        self._session = session
26
        self._prefix = prefix
27
        self._args = args
28
29
    def __getattr__(self, name):
30
        return functools.partial(self._session.request,self._prefix + name, *self._args)
31
32
class RemoteMap(object):
33
34 6
    """Represents a string->object map stored in Nvim.
35
36 6
    This is the dict counterpart to the `RemoteSequence` class, but it is used
37 6
    as a generic way of retrieving values from the various map-like data
38 6
    structures present in Nvim.
39 6
40
    It is used to provide a dict-like API to vim variables and options.
41 6
    """
42
43 6
    def __init__(self, session, get_method, set_method, self_obj=None):
44
        """Initialize a RemoteMap with session, getter/setter and self_obj."""
45 6
        self._get = _wrap(session, get_method, self_obj)
46
        self._set = None
47 6
        if set_method:
48
            self._set = _wrap(session, set_method, self_obj)
49 6
50
    def __getitem__(self, key):
51 6
        """Return a map value by key."""
52
        return self._get(key)
53 6
54 6
    def __setitem__(self, key, value):
55
        """Set a map value by key(if the setter was provided)."""
56
        if not self._set:
57 6
            raise TypeError('This dict is read-only')
58
        self._set(key, value)
59 6
60
    def __delitem__(self, key):
61
        """Delete a map value by associating None with the key."""
62
        if not self._set:
63
            raise TypeError('This dict is read-only')
64
        return self._set(key, None)
65 6
66
    def __contains__(self, key):
67
        """Check if key is present in the map."""
68
        try:
69
            self._get(key)
70
            return True
71
        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...
72
            return False
73 6
74
    def get(self, key, default=None):
75
        """Return value for key if present, else a default value."""
76
        try:
77
            return self._get(key)
78
        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...
79
            return default
80
81
82
class RemoteSequence(object):
83
84
    """Represents a sequence of objects stored in Nvim.
85
86
    This class is used to wrap msgapck-rpc functions that work on Nvim
87
    sequences(of lines, buffers, windows and tabpages) with an API that
88
    is similar to the one provided by the python-vim interface.
89
90
    For example, the 'buffers' property of the `Nvim class is a RemoteSequence
91
    sequence instance, and the expression `nvim.buffers[0]` is translated to
92
    session.request('vim_get_buffers')[0].
93
94 6
    It can also receive an optional self_obj that will be passed as first
95
    argument of the request. For example, `tabpage.windows[0]` is translated
96 6
    to: session.request('tabpage_get_windows', tabpage_instance)[0].
97
98 6
    One important detail about this class is that all methods will fetch the
99
    sequence into a list and perform the necessary manipulation
100 6
    locally(iteration, indexing, counting, etc).
101
    """
102 6
103
    def __init__(self, session, method, self_obj=None):
104 6
        """Initialize a RemoteSequence with session, method and self_obj."""
105 6
        self._fetch = _wrap(session, method, self_obj)
106
107
    def __len__(self):
108 6
        """Return the length of the remote sequence."""
109
        return len(self._fetch())
110 6
111 6
    def __getitem__(self, idx):
112 6
        """Return a sequence item by index."""
113
        if not isinstance(idx, slice):
114 6
            return self._fetch()[idx]
115
        return self._fetch()[idx.start:idx.stop]
116 6
117
    def __iter__(self):
118
        """Return an iterator for the sequence."""
119 6
        items = self._fetch()
120
        for item in items:
121
            yield item
122
123 6
    def __contains__(self, item):
124
        """Check if an item is present in the sequence."""
125
        return item in self._fetch()
126
127
128
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 kind seems to be unused.
Loading history...
Unused Code introduced by
The argument method seems to be unused.
Loading history...
129
    return obj
130
131
132
133
class DecodeHook(object):
134
135
    """SessionHook subclass that decodes utf-8 strings coming from Nvim.
136
137
    This class is useful for python3, where strings are now unicode by
138
    default(byte strings need to be prefixed with "b").
139
    """
140
141
    def __init__(self, encoding='utf-8', encoding_errors='strict'):
142 6
        """Initialize with encoding and encoding errors policy."""
143
        self.encoding = encoding
144 6
        self.encoding_errors = encoding_errors
145 6
146
    def decode_if_bytes(self, obj):
147 6
        if isinstance(obj, bytes):
148
            return obj.decode(self.encoding, errors=self.encoding_errors)
149
        return obj
150
151
    def walk(self, obj):
152
        """Decode bytes found in obj (any msgpack object).
153 3
154 3
        Uses encoding and policy specified in constructor.
155 3
        """
156 3
        return walk(self.decode_if_bytes, obj)
157
158 3
def walk(fn, obj, *args):
159
    """Recursively walk an object graph applying `fn`/`args` to objects."""
160 3
    if type(obj) in [list, tuple]:
161
        return list(walk(fn, o, *args) for o in obj)
162
    if type(obj) is dict:
163
        return dict((walk(fn, k, *args), walk(fn, v, *args)) for k, v in
164 6
                    obj.items())
165
    return fn(obj, *args)
166
167
168
def _wrap(session, method, self_obj):
169
    if self_obj is not None:
170
        return lambda *args: session.request(method, self_obj, *args)
171
    else:
172
        return lambda *args: session.request(method, *args)
173