Completed
Pull Request — master (#179)
by Björn
24:47
created

neovim.api._identity()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
cc 1
dl 0
loc 2
ccs 0
cts 0
cp 0
crap 2
rs 10
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 __init__(self, session, code_data):
15 6
        """Initialize from session and code_data immutable object.
16
17
        The `code_data` contains serialization information required for
18 6
        msgpack-rpc calls. It must be immutable for Buffer equality to work.
19
        """
20 6
        self._session = session
21
        self.code_data = code_data
22
        self.api = RemoteApi(self, self._api_prefix)
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _api_prefix.

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...
23 6
        self.vars = RemoteMap(self, self._api_prefix + 'get_var',
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _api_prefix.

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...
24
                              self._api_prefix + 'set_var')
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _api_prefix.

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...
25
        self.options = RemoteMap(self, self._api_prefix + 'get_option',
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _api_prefix.

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
                                 self._api_prefix + 'set_option')
0 ignored issues
show
Bug introduced by
The Instance of Remote does not seem to have a member named _api_prefix.

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