New upstream version 0.4.2
Thomas Goirand
3 years ago
0 | 0 | Metadata-Version: 2.1 |
1 | 1 | Name: pynvim |
2 | Version: 0.4.0 | |
2 | Version: 0.4.2 | |
3 | 3 | Summary: Python client to neovim |
4 | Home-page: http://github.com/neovim/python-client | |
4 | Home-page: http://github.com/neovim/pynvim | |
5 | 5 | Author: Thiago de Arruda |
6 | 6 | Author-email: tpadilha84@gmail.com |
7 | 7 | License: Apache |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.4.0.tar.gz | |
8 | Download-URL: https://github.com/neovim/pynvim/archive/0.4.2.tar.gz | |
9 | 9 | Description: UNKNOWN |
10 | 10 | Platform: UNKNOWN |
11 | 11 | Provides-Extra: pyuv |
0 | ### Pynvim: Python client to [Neovim](https://github.com/neovim/neovim) | |
0 | Pynvim: Python client to [Neovim](https://github.com/neovim/neovim) | |
1 | =================================================================== | |
1 | 2 | |
2 | 3 | [![Build Status](https://travis-ci.org/neovim/pynvim.svg?branch=master)](https://travis-ci.org/neovim/pynvim) |
3 | 4 | [![Documentation Status](https://readthedocs.org/projects/pynvim/badge/?version=latest)](http://pynvim.readthedocs.io/en/latest/?badge=latest) |
4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/neovim/pynvim/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/neovim/pynvim/?branch=master) | |
5 | [![Code Coverage](https://scrutinizer-ci.com/g/neovim/pynvim/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/neovim/pynvim/?branch=master) | |
5 | [![Code coverage](https://codecov.io/gh/neovim/pynvim/branch/master/graph/badge.svg)](https://codecov.io/gh/neovim/pynvim) | |
6 | 6 | |
7 | 7 | Pynvim implements support for python plugins in Nvim. It also works as a library for |
8 | 8 | connecting to and scripting Nvim processes through its msgpack-rpc API. |
9 | 9 | |
10 | #### Installation | |
10 | Install | |
11 | ------- | |
11 | 12 | |
12 | 13 | Supports python 2.7, and 3.4 or later. |
13 | 14 | |
33 | 34 | pip3 install . |
34 | 35 | ``` |
35 | 36 | |
36 | #### Python Plugin API | |
37 | Python Plugin API | |
38 | ----------------- | |
37 | 39 | |
38 | 40 | Pynvim supports python _remote plugins_ (via the language-agnostic Nvim rplugin |
39 | 41 | interface), as well as _Vim plugins_ (via the `:python[3]` interface). Thus when |
53 | 55 | |
54 | 56 | See the [Python Plugin API](http://pynvim.readthedocs.io/en/latest/usage/python-plugin-api.html) documentation for usage of this new functionality. |
55 | 57 | |
56 | #### Development | |
58 | Development | |
59 | ----------- | |
57 | 60 | |
58 | 61 | Use (and activate) a local virtualenv. |
59 | 62 | |
73 | 76 | [development](http://pynvim.readthedocs.io/en/latest/development.html) |
74 | 77 | documentation. |
75 | 78 | |
76 | #### Usage through the python REPL | |
79 | ### Usage from the Python REPL | |
77 | 80 | |
78 | 81 | A number of different transports are supported, but the simplest way to get |
79 | 82 | started is with the python REPL. First, start Nvim with a known address (or use |
116 | 119 | continuing startup. |
117 | 120 | |
118 | 121 | See the tests for more examples. |
122 | ||
123 | Release | |
124 | ------- | |
125 | ||
126 | 1. Create a release commit with title `Pynvim x.y.z` | |
127 | - list significant changes in the commit message | |
128 | - bump the version in `pynvim/util.py` and `setup.py` (3 places in total) | |
129 | 2. Make a release on GitHub with the same commit/version tag and copy the message. | |
130 | 3. Run `scripts/disable_log_statements.sh` | |
131 | 4. Run `python setup.py sdist` | |
132 | - diff the release tarball `dist/pynvim-x.y.z.tar.gz` against the previous one. | |
133 | 5. Run `twine upload -r pypi dist/pynvim-x.y.z.tar.gz` | |
134 | - Assumes you have a pypi account with permissions. | |
135 | 6. Run `scripts/enable_log_statements.sh` or `git reset --hard` to restore the working dir. |
5 | 5 | import os |
6 | 6 | import sys |
7 | 7 | |
8 | from .api import Nvim, NvimError | |
9 | from .compat import IS_PYTHON3 | |
10 | from .msgpack_rpc import (ErrorResponse, child_session, socket_session, | |
11 | stdio_session, tcp_session) | |
12 | from .plugin import (Host, autocmd, command, decode, encoding, function, | |
13 | plugin, rpc_export, shutdown_hook) | |
14 | from .util import VERSION, Version | |
8 | from pynvim.api import Nvim, NvimError | |
9 | from pynvim.compat import IS_PYTHON3 | |
10 | from pynvim.msgpack_rpc import (ErrorResponse, child_session, socket_session, | |
11 | stdio_session, tcp_session) | |
12 | from pynvim.plugin import (Host, autocmd, command, decode, encoding, function, | |
13 | plugin, rpc_export, shutdown_hook) | |
14 | from pynvim.util import VERSION, Version | |
15 | 15 | |
16 | 16 | |
17 | 17 | __all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session', |
3 | 3 | instances. |
4 | 4 | """ |
5 | 5 | |
6 | from .buffer import Buffer | |
7 | from .common import decode_if_bytes, walk | |
8 | from .nvim import Nvim, NvimError | |
9 | from .tabpage import Tabpage | |
10 | from .window import Window | |
6 | from pynvim.api.buffer import Buffer | |
7 | from pynvim.api.common import decode_if_bytes, walk | |
8 | from pynvim.api.nvim import Nvim, NvimError | |
9 | from pynvim.api.tabpage import Tabpage | |
10 | from pynvim.api.window import Window | |
11 | 11 | |
12 | 12 | |
13 | 13 | __all__ = ('Nvim', 'Buffer', 'Window', 'Tabpage', 'NvimError', |
0 | 0 | """API for working with a Nvim Buffer.""" |
1 | from .common import Remote | |
2 | from ..compat import IS_PYTHON3, check_async | |
1 | from pynvim.api.common import Remote | |
2 | from pynvim.compat import IS_PYTHON3, check_async | |
3 | 3 | |
4 | 4 | |
5 | 5 | __all__ = ('Buffer') |
2 | 2 | |
3 | 3 | from msgpack import unpackb |
4 | 4 | |
5 | from ..compat import unicode_errors_default | |
5 | from pynvim.compat import unicode_errors_default | |
6 | 6 | |
7 | 7 | __all__ = () |
8 | 8 |
6 | 6 | |
7 | 7 | from msgpack import ExtType |
8 | 8 | |
9 | from .buffer import Buffer | |
10 | from .common import (NvimError, Remote, RemoteApi, RemoteMap, RemoteSequence, | |
11 | decode_if_bytes, walk) | |
12 | from .tabpage import Tabpage | |
13 | from .window import Window | |
14 | from ..compat import IS_PYTHON3 | |
15 | from ..util import Version, format_exc_skip | |
9 | from pynvim.api.buffer import Buffer | |
10 | from pynvim.api.common import (NvimError, Remote, RemoteApi, RemoteMap, RemoteSequence, | |
11 | decode_if_bytes, walk) | |
12 | from pynvim.api.tabpage import Tabpage | |
13 | from pynvim.api.window import Window | |
14 | from pynvim.compat import IS_PYTHON3 | |
15 | from pynvim.util import Version, format_exc_skip | |
16 | 16 | |
17 | 17 | __all__ = ('Nvim') |
18 | 18 | |
419 | 419 | """ |
420 | 420 | try: |
421 | 421 | self.command(quit_command) |
422 | except IOError: | |
422 | except OSError: | |
423 | 423 | # sending a quit command will raise an IOError because the |
424 | 424 | # connection is closed before a response is received. Safe to |
425 | 425 | # ignore it. |
0 | 0 | """API for working with Nvim tabpages.""" |
1 | from .common import Remote, RemoteSequence | |
1 | from pynvim.api.common import Remote, RemoteSequence | |
2 | 2 | |
3 | 3 | |
4 | 4 | __all__ = ('Tabpage') |
0 | 0 | """API for working with Nvim windows.""" |
1 | from .common import Remote | |
1 | from pynvim.api.common import Remote | |
2 | 2 | |
3 | 3 | |
4 | 4 | __all__ = ('Window') |
33 | 33 | unicode_errors_default = 'surrogateescape' |
34 | 34 | else: |
35 | 35 | find_module = original_find_module |
36 | unicode_errors_default = 'strict' | |
36 | unicode_errors_default = 'ignore' | |
37 | 37 | |
38 | 38 | NUM_TYPES = (int, long, float) |
39 | 39 |
3 | 3 | handling some Nvim particularities(server->client requests for example), the |
4 | 4 | code here should work with other msgpack-rpc servers. |
5 | 5 | """ |
6 | from .async_session import AsyncSession | |
7 | from .event_loop import EventLoop | |
8 | from .msgpack_stream import MsgpackStream | |
9 | from .session import ErrorResponse, Session | |
10 | from ..util import get_client_info | |
6 | from pynvim.msgpack_rpc.async_session import AsyncSession | |
7 | from pynvim.msgpack_rpc.event_loop import EventLoop | |
8 | from pynvim.msgpack_rpc.msgpack_stream import MsgpackStream | |
9 | from pynvim.msgpack_rpc.session import ErrorResponse, Session | |
10 | from pynvim.util import get_client_info | |
11 | 11 | |
12 | 12 | |
13 | 13 | __all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session', |
79 | 79 | self._handlers.get(msg[0], self._on_invalid_message)(msg) |
80 | 80 | except Exception: |
81 | 81 | err_str = format_exc(5) |
82 | warn(err_str) | |
82 | pass # replaces next logging statement | |
83 | #warn(err_str) | |
83 | 84 | self._msgpack_stream.send([1, 0, err_str, None]) |
84 | 85 | |
85 | 86 | def _on_request(self, msg): |
87 | 88 | # - msg[1]: id |
88 | 89 | # - msg[2]: method name |
89 | 90 | # - msg[3]: arguments |
90 | debug('received request: %s, %s', msg[2], msg[3]) | |
91 | pass # replaces next logging statement | |
92 | #debug('received request: %s, %s', msg[2], msg[3]) | |
91 | 93 | self._request_cb(msg[2], msg[3], Response(self._msgpack_stream, |
92 | 94 | msg[1])) |
93 | 95 | |
96 | 98 | # - msg[1]: the id |
97 | 99 | # - msg[2]: error(if any) |
98 | 100 | # - msg[3]: result(if not errored) |
99 | debug('received response: %s, %s', msg[2], msg[3]) | |
101 | pass # replaces next logging statement | |
102 | #debug('received response: %s, %s', msg[2], msg[3]) | |
100 | 103 | self._pending_requests.pop(msg[1])(msg[2], msg[3]) |
101 | 104 | |
102 | 105 | def _on_notification(self, msg): |
103 | 106 | # notification/event |
104 | 107 | # - msg[1]: event name |
105 | 108 | # - msg[2]: arguments |
106 | debug('received notification: %s, %s', msg[1], msg[2]) | |
109 | pass # replaces next logging statement | |
110 | #debug('received notification: %s, %s', msg[1], msg[2]) | |
107 | 111 | self._notification_cb(msg[1], msg[2]) |
108 | 112 | |
109 | 113 | def _on_invalid_message(self, msg): |
110 | 114 | error = 'Received invalid message %s' % msg |
111 | warn(error) | |
115 | pass # replaces next logging statement | |
116 | #warn(error) | |
112 | 117 | self._msgpack_stream.send([1, 0, error, None]) |
113 | 118 | |
114 | 119 | |
134 | 139 | resp = [1, self._request_id, value, None] |
135 | 140 | else: |
136 | 141 | resp = [1, self._request_id, None, value] |
137 | debug('sending response to request %d: %s', self._request_id, resp) | |
142 | pass # replaces next logging statement | |
143 | #debug('sending response to request %d: %s', self._request_id, resp) | |
138 | 144 | self._msgpack_stream.send(resp) |
2 | 2 | Tries to use pyuv as a backend, falling back to the asyncio implementation. |
3 | 3 | """ |
4 | 4 | |
5 | from ...compat import IS_PYTHON3 | |
5 | from pynvim.compat import IS_PYTHON3 | |
6 | 6 | |
7 | 7 | # on python3 we only support asyncio, as we expose it to plugins |
8 | 8 | if IS_PYTHON3: |
9 | from .asyncio import AsyncioEventLoop | |
9 | from pynvim.msgpack_rpc.event_loop.asyncio import AsyncioEventLoop | |
10 | 10 | EventLoop = AsyncioEventLoop |
11 | 11 | else: |
12 | 12 | try: |
13 | 13 | # libuv is fully implemented in C, use it when available |
14 | from .uv import UvEventLoop | |
14 | from pynvim.msgpack_rpc.event_loop.uv import UvEventLoop | |
15 | 15 | EventLoop = UvEventLoop |
16 | 16 | except ImportError: |
17 | 17 | # asyncio(trollius on python 2) is pure python and should be more |
18 | 18 | # portable across python implementations |
19 | from .asyncio import AsyncioEventLoop | |
19 | from pynvim.msgpack_rpc.event_loop.asyncio import AsyncioEventLoop | |
20 | 20 | EventLoop = AsyncioEventLoop |
21 | 21 | |
22 | 22 |
21 | 21 | # Fallback to trollius |
22 | 22 | import trollius as asyncio |
23 | 23 | |
24 | from .base import BaseEventLoop | |
24 | from pynvim.msgpack_rpc.event_loop.base import BaseEventLoop | |
25 | 25 | |
26 | 26 | logger = logging.getLogger(__name__) |
27 | 27 | debug, info, warn = (logger.debug, logger.info, logger.warning,) |
101 | 101 | pipe = sys.stdin |
102 | 102 | coroutine = self._loop.connect_read_pipe(self._fact, pipe) |
103 | 103 | self._loop.run_until_complete(coroutine) |
104 | debug("native stdin connection successful") | |
104 | pass # replaces next logging statement | |
105 | #debug("native stdin connection successful") | |
105 | 106 | |
106 | 107 | # Make sure subprocesses don't clobber stdout, |
107 | 108 | # send the output to stderr instead. |
114 | 115 | pipe = os.fdopen(rename_stdout, 'wb') |
115 | 116 | coroutine = self._loop.connect_write_pipe(self._fact, pipe) |
116 | 117 | self._loop.run_until_complete(coroutine) |
117 | debug("native stdout connection successful") | |
118 | pass # replaces next logging statement | |
119 | #debug("native stdout connection successful") | |
118 | 120 | |
119 | 121 | def _connect_child(self, argv): |
120 | 122 | if os.name != 'nt': |
94 | 94 | |
95 | 95 | def connect_tcp(self, address, port): |
96 | 96 | """Connect to tcp/ip `address`:`port`. Delegated to `_connect_tcp`.""" |
97 | info('Connecting to TCP address: %s:%d', address, port) | |
97 | pass # replaces next logging statement | |
98 | #info('Connecting to TCP address: %s:%d', address, port) | |
98 | 99 | self._connect_tcp(address, port) |
99 | 100 | |
100 | 101 | def connect_socket(self, path): |
101 | 102 | """Connect to socket at `path`. Delegated to `_connect_socket`.""" |
102 | info('Connecting to %s', path) | |
103 | pass # replaces next logging statement | |
104 | #info('Connecting to %s', path) | |
103 | 105 | self._connect_socket(path) |
104 | 106 | |
105 | 107 | def connect_stdio(self): |
106 | 108 | """Connect using stdin/stdout. Delegated to `_connect_stdio`.""" |
107 | info('Preparing stdin/stdout for streaming data') | |
109 | pass # replaces next logging statement | |
110 | #info('Preparing stdin/stdout for streaming data') | |
108 | 111 | self._connect_stdio() |
109 | 112 | |
110 | 113 | def connect_child(self, argv): |
111 | 114 | """Connect a new Nvim instance. Delegated to `_connect_child`.""" |
112 | info('Spawning a new nvim instance') | |
115 | pass # replaces next logging statement | |
116 | #info('Spawning a new nvim instance') | |
113 | 117 | self._connect_child(argv) |
114 | 118 | |
115 | 119 | def send(self, data): |
116 | 120 | """Queue `data` for sending to Nvim.""" |
117 | debug("Sending '%s'", data) | |
121 | pass # replaces next logging statement | |
122 | #debug("Sending '%s'", data) | |
118 | 123 | self._send(data) |
119 | 124 | |
120 | 125 | def threadsafe_call(self, fn): |
139 | 144 | self._on_data = data_cb |
140 | 145 | if threading.current_thread() == main_thread: |
141 | 146 | self._setup_signals([signal.SIGINT, signal.SIGTERM]) |
142 | debug('Entering event loop') | |
147 | pass # replaces next logging statement | |
148 | #debug('Entering event loop') | |
143 | 149 | self._run() |
144 | debug('Exited event loop') | |
150 | pass # replaces next logging statement | |
151 | #debug('Exited event loop') | |
145 | 152 | if threading.current_thread() == main_thread: |
146 | 153 | self._teardown_signals() |
147 | 154 | signal.signal(signal.SIGINT, default_int_handler) |
150 | 157 | def stop(self): |
151 | 158 | """Stop the event loop.""" |
152 | 159 | self._stop() |
153 | debug('Stopped event loop') | |
160 | pass # replaces next logging statement | |
161 | #debug('Stopped event loop') | |
154 | 162 | |
155 | 163 | def close(self): |
156 | 164 | """Stop the event loop.""" |
157 | 165 | self._close() |
158 | debug('Closed event loop') | |
166 | pass # replaces next logging statement | |
167 | #debug('Closed event loop') | |
159 | 168 | |
160 | 169 | def _on_signal(self, signum): |
161 | 170 | msg = 'Received {}'.format(self._signames[signum]) |
162 | debug(msg) | |
171 | pass # replaces next logging statement | |
172 | #debug(msg) | |
163 | 173 | if signum == signal.SIGINT and self._transport_type == 'stdio': |
164 | 174 | # When the transport is stdio, we are probably running as a Nvim |
165 | 175 | # child process. In that case, we don't want to be killed by |
172 | 182 | self.stop() |
173 | 183 | |
174 | 184 | def _on_error(self, error): |
175 | debug(error) | |
176 | self._error = IOError(error) | |
185 | pass # replaces next logging statement | |
186 | #debug(error) | |
187 | self._error = OSError(error) | |
177 | 188 | self.stop() |
178 | 189 | |
179 | 190 | def _on_interrupt(self): |
3 | 3 | |
4 | 4 | import pyuv |
5 | 5 | |
6 | from .base import BaseEventLoop | |
6 | from pynvim.msgpack_rpc.event_loop.base import BaseEventLoop | |
7 | 7 | |
8 | 8 | |
9 | 9 | class UvEventLoop(BaseEventLoop): |
22 | 22 | if error: |
23 | 23 | msg = 'Cannot connect to {}: {}'.format( |
24 | 24 | self._connect_address, pyuv.errno.strerror(error)) |
25 | self._connection_error = IOError(msg) | |
25 | self._connection_error = OSError(msg) | |
26 | 26 | return |
27 | 27 | self._read_stream = self._write_stream = stream |
28 | 28 | |
44 | 44 | self._on_error('EOF') |
45 | 45 | |
46 | 46 | def _disconnected(self, *args): |
47 | raise IOError('Not connected to Nvim') | |
47 | raise OSError('Not connected to Nvim') | |
48 | 48 | |
49 | 49 | def _connect_tcp(self, address, port): |
50 | 50 | stream = pyuv.TCP(self._loop) |
2 | 2 | |
3 | 3 | from msgpack import Packer, Unpacker |
4 | 4 | |
5 | from ..compat import unicode_errors_default | |
5 | from pynvim.compat import unicode_errors_default | |
6 | 6 | |
7 | 7 | logger = logging.getLogger(__name__) |
8 | 8 | debug, info, warn = (logger.debug, logger.info, logger.warning,) |
20 | 20 | """Wrap `event_loop` on a msgpack-aware interface.""" |
21 | 21 | self.loop = event_loop |
22 | 22 | self._packer = Packer(unicode_errors=unicode_errors_default) |
23 | self._unpacker = Unpacker() | |
23 | self._unpacker = Unpacker(unicode_errors=unicode_errors_default) | |
24 | 24 | self._message_cb = None |
25 | 25 | |
26 | 26 | def threadsafe_call(self, fn): |
29 | 29 | |
30 | 30 | def send(self, msg): |
31 | 31 | """Queue `msg` for sending to Nvim.""" |
32 | debug('sent %s', msg) | |
32 | pass # replaces next logging statement | |
33 | #debug('sent %s', msg) | |
33 | 34 | self.loop.send(self._packer.pack(msg)) |
34 | 35 | |
35 | 36 | def run(self, message_cb): |
54 | 55 | self._unpacker.feed(data) |
55 | 56 | while True: |
56 | 57 | try: |
57 | debug('waiting for message...') | |
58 | pass # replaces next logging statement | |
59 | #debug('waiting for message...') | |
58 | 60 | msg = next(self._unpacker) |
59 | debug('received message: %s', msg) | |
61 | pass # replaces next logging statement | |
62 | #debug('received message: %s', msg) | |
60 | 63 | self._message_cb(msg) |
61 | 64 | except StopIteration: |
62 | debug('unpacker needs more data...') | |
65 | pass # replaces next logging statement | |
66 | #debug('unpacker needs more data...') | |
63 | 67 | break |
5 | 5 | |
6 | 6 | import greenlet |
7 | 7 | |
8 | from ..compat import check_async | |
8 | from pynvim.compat import check_async | |
9 | 9 | |
10 | 10 | logger = logging.getLogger(__name__) |
11 | 11 | error, debug, info, warn = (logger.error, logger.debug, logger.info, |
37 | 37 | try: |
38 | 38 | fn(*args, **kwargs) |
39 | 39 | except Exception: |
40 | warn("error caught while excecuting async callback\n%s\n", | |
41 | format_exc()) | |
40 | pass # replaces next logging statement | |
41 | #warn("error caught while excecuting async callback\n%s\n", | |
42 | #format_exc()) | |
42 | 43 | |
43 | 44 | def greenlet_wrapper(): |
44 | 45 | gr = greenlet.greenlet(handler) |
94 | 95 | v = self._blocking_request(method, args) |
95 | 96 | if not v: |
96 | 97 | # EOF |
97 | raise IOError('EOF') | |
98 | raise OSError('EOF') | |
98 | 99 | err, rv = v |
99 | 100 | if err: |
100 | info("'Received error: %s", err) | |
101 | pass # replaces next logging statement | |
102 | #info("'Received error: %s", err) | |
101 | 103 | raise self.error_wrapper(err) |
102 | 104 | return rv |
103 | 105 | |
126 | 128 | gr.switch() |
127 | 129 | |
128 | 130 | if self._setup_exception: |
129 | error('Setup error: {}'.format(self._setup_exception)) | |
131 | pass # replaces next logging statement | |
132 | #error('Setup error: {}'.format(self._setup_exception)) | |
130 | 133 | raise self._setup_exception |
131 | 134 | |
132 | 135 | # Process all pending requests and notifications |
155 | 158 | parent = gr.parent |
156 | 159 | |
157 | 160 | def response_cb(err, rv): |
158 | debug('response is available for greenlet %s, switching back', gr) | |
161 | pass # replaces next logging statement | |
162 | #debug('response is available for greenlet %s, switching back', gr) | |
159 | 163 | gr.switch(err, rv) |
160 | 164 | |
161 | 165 | self._async_session.request(method, args, response_cb) |
162 | debug('yielding from greenlet %s to wait for response', gr) | |
166 | pass # replaces next logging statement | |
167 | #debug('yielding from greenlet %s to wait for response', gr) | |
163 | 168 | return parent.switch() |
164 | 169 | |
165 | 170 | def _blocking_request(self, method, args): |
192 | 197 | def handler(): |
193 | 198 | try: |
194 | 199 | rv = self._request_cb(name, args) |
195 | debug('greenlet %s finished executing, ' | |
196 | + 'sending %s as response', gr, rv) | |
200 | pass # replaces next logging statement | |
201 | #debug('greenlet %s finished executing, ' | |
202 | #+ 'sending %s as response', gr, rv) | |
197 | 203 | response.send(rv) |
198 | 204 | except ErrorResponse as err: |
199 | warn("error response from request '%s %s': %s", name, | |
200 | args, format_exc()) | |
205 | pass # replaces next logging statement | |
206 | #warn("error response from request '%s %s': %s", name, | |
207 | #args, format_exc()) | |
201 | 208 | response.send(err.args[0], error=True) |
202 | 209 | except Exception as err: |
203 | warn("error caught while processing request '%s %s': %s", name, | |
204 | args, format_exc()) | |
210 | pass # replaces next logging statement | |
211 | #warn("error caught while processing request '%s %s': %s", name, | |
212 | #args, format_exc()) | |
205 | 213 | response.send(repr(err) + "\n" + format_exc(5), error=True) |
206 | debug('greenlet %s is now dying...', gr) | |
214 | pass # replaces next logging statement | |
215 | #debug('greenlet %s is now dying...', gr) | |
207 | 216 | |
208 | 217 | # Create a new greenlet to handle the request |
209 | 218 | gr = greenlet.greenlet(handler) |
210 | debug('received rpc request, greenlet %s will handle it', gr) | |
219 | pass # replaces next logging statement | |
220 | #debug('received rpc request, greenlet %s will handle it', gr) | |
211 | 221 | gr.switch() |
212 | 222 | |
213 | 223 | def _on_notification(self, name, args): |
214 | 224 | def handler(): |
215 | 225 | try: |
216 | 226 | self._notification_cb(name, args) |
217 | debug('greenlet %s finished executing', gr) | |
227 | pass # replaces next logging statement | |
228 | #debug('greenlet %s finished executing', gr) | |
218 | 229 | except Exception: |
219 | warn("error caught while processing notification '%s %s': %s", | |
220 | name, args, format_exc()) | |
221 | ||
222 | debug('greenlet %s is now dying...', gr) | |
230 | pass # replaces next logging statement | |
231 | #warn("error caught while processing notification '%s %s': %s", | |
232 | #name, args, format_exc()) | |
233 | ||
234 | pass # replaces next logging statement | |
235 | #debug('greenlet %s is now dying...', gr) | |
223 | 236 | |
224 | 237 | gr = greenlet.greenlet(handler) |
225 | debug('received rpc notification, greenlet %s will handle it', gr) | |
238 | pass # replaces next logging statement | |
239 | #debug('received rpc notification, greenlet %s will handle it', gr) | |
226 | 240 | gr.switch() |
227 | 241 | |
228 | 242 |
0 | 0 | """Nvim plugin/host subpackage.""" |
1 | 1 | |
2 | from .decorators import (autocmd, command, decode, encoding, function, | |
3 | plugin, rpc_export, shutdown_hook) | |
4 | from .host import Host | |
2 | from pynvim.plugin.decorators import (autocmd, command, decode, encoding, function, | |
3 | plugin, rpc_export, shutdown_hook) | |
4 | from pynvim.plugin.host import Host | |
5 | 5 | |
6 | 6 | |
7 | 7 | __all__ = ('Host', 'plugin', 'rpc_export', 'command', 'autocmd', |
2 | 2 | import inspect |
3 | 3 | import logging |
4 | 4 | |
5 | from ..compat import IS_PYTHON3, unicode_errors_default | |
5 | from pynvim.compat import IS_PYTHON3, unicode_errors_default | |
6 | 6 | |
7 | 7 | logger = logging.getLogger(__name__) |
8 | 8 | debug, info, warn = (logger.debug, logger.info, logger.warning,) |
7 | 7 | from functools import partial |
8 | 8 | from traceback import format_exc |
9 | 9 | |
10 | from . import script_host | |
11 | from ..api import decode_if_bytes, walk | |
12 | from ..compat import IS_PYTHON3, find_module | |
13 | from ..msgpack_rpc import ErrorResponse | |
14 | from ..util import format_exc_skip, get_client_info | |
10 | from pynvim.api import decode_if_bytes, walk | |
11 | from pynvim.compat import IS_PYTHON3, find_module | |
12 | from pynvim.msgpack_rpc import ErrorResponse | |
13 | from pynvim.plugin import script_host | |
14 | from pynvim.util import format_exc_skip, get_client_info | |
15 | 15 | |
16 | 16 | __all__ = ('Host') |
17 | 17 | |
58 | 58 | errmsg = "{}: Async request caused an error:\n{}\n".format( |
59 | 59 | self.name, decode_if_bytes(msg)) |
60 | 60 | self.nvim.err_write(errmsg, async_=True) |
61 | return errmsg | |
61 | 62 | |
62 | 63 | def start(self, plugins): |
63 | 64 | """Start listening for msgpack-rpc requests and notifications.""" |
115 | 116 | handler = self._request_handlers.get(name, None) |
116 | 117 | if not handler: |
117 | 118 | msg = self._missing_handler_error(name, 'request') |
118 | error(msg) | |
119 | pass # replaces next logging statement | |
120 | #error(msg) | |
119 | 121 | raise ErrorResponse(msg) |
120 | 122 | |
121 | debug('calling request handler for "%s", args: "%s"', name, args) | |
123 | pass # replaces next logging statement | |
124 | #debug('calling request handler for "%s", args: "%s"', name, args) | |
122 | 125 | rv = handler(*args) |
123 | debug("request handler for '%s %s' returns: %s", name, args, rv) | |
126 | pass # replaces next logging statement | |
127 | #debug("request handler for '%s %s' returns: %s", name, args, rv) | |
124 | 128 | return rv |
125 | 129 | |
126 | 130 | def _on_notification(self, name, args): |
130 | 134 | handler = self._notification_handlers.get(name, None) |
131 | 135 | if not handler: |
132 | 136 | msg = self._missing_handler_error(name, 'notification') |
133 | error(msg) | |
137 | pass # replaces next logging statement | |
138 | #error(msg) | |
134 | 139 | self._on_async_err(msg + "\n") |
135 | 140 | return |
136 | 141 | |
137 | debug('calling notification handler for "%s", args: "%s"', name, args) | |
142 | pass # replaces next logging statement | |
143 | #debug('calling notification handler for "%s", args: "%s"', name, args) | |
138 | 144 | handler(*args) |
139 | 145 | |
140 | 146 | def _missing_handler_error(self, name, kind): |
151 | 157 | for path in plugins: |
152 | 158 | err = None |
153 | 159 | if path in self._loaded: |
154 | error('{} is already loaded'.format(path)) | |
160 | pass # replaces next logging statement | |
161 | #error('{} is already loaded'.format(path)) | |
155 | 162 | continue |
156 | 163 | try: |
157 | 164 | if path == "script_host.py": |
165 | 172 | self._discover_classes(module, handlers, path) |
166 | 173 | self._discover_functions(module, handlers, path, False) |
167 | 174 | if not handlers: |
168 | error('{} exports no handlers'.format(path)) | |
175 | pass # replaces next logging statement | |
176 | #error('{} exports no handlers'.format(path)) | |
169 | 177 | continue |
170 | 178 | self._loaded[path] = {'handlers': handlers, 'module': module} |
171 | 179 | except Exception as e: |
172 | 180 | err = ('Encountered {} loading plugin at {}: {}\n{}' |
173 | 181 | .format(type(e).__name__, path, e, format_exc(5))) |
174 | error(err) | |
182 | pass # replaces next logging statement | |
183 | #error(err) | |
175 | 184 | self._load_errors[path] = err |
176 | 185 | |
177 | 186 | kind = ("script-host" if len(plugins) == 1 and has_script |
178 | 187 | else "rplugin-host") |
179 | self.nvim.api.set_client_info( | |
180 | *get_client_info(kind, 'host', host_method_spec), | |
181 | async_=True) | |
188 | info = get_client_info(kind, 'host', host_method_spec) | |
189 | self.name = info[0] | |
190 | self.nvim.api.set_client_info(*info, async_=True) | |
182 | 191 | |
183 | 192 | def _unload(self): |
184 | 193 | for path, plugin in self._loaded.items(): |
3 | 3 | import logging |
4 | 4 | import os |
5 | 5 | import sys |
6 | ||
7 | from .decorators import plugin, rpc_export | |
8 | from ..api import Nvim, walk | |
9 | from ..compat import IS_PYTHON3 | |
10 | from ..msgpack_rpc import ErrorResponse | |
11 | from ..util import format_exc_skip | |
6 | from types import ModuleType | |
7 | ||
8 | from pynvim.api import Nvim, walk | |
9 | from pynvim.compat import IS_PYTHON3 | |
10 | from pynvim.msgpack_rpc import ErrorResponse | |
11 | from pynvim.plugin.decorators import plugin, rpc_export | |
12 | from pynvim.util import format_exc_skip | |
12 | 13 | |
13 | 14 | __all__ = ('ScriptHost',) |
14 | 15 | |
36 | 37 | """Initialize the legacy python-vim environment.""" |
37 | 38 | self.setup(nvim) |
38 | 39 | # context where all code will run |
39 | self.module = imp.new_module('__main__') | |
40 | self.module = ModuleType('__main__') | |
40 | 41 | nvim.script_context = self.module |
41 | 42 | # it seems some plugins assume 'sys' is already imported, so do it now |
42 | 43 | exec('import sys', self.module.__dict__) |
43 | 44 | self.legacy_vim = LegacyVim.from_nvim(nvim) |
44 | 45 | sys.modules['vim'] = self.legacy_vim |
45 | ||
46 | # mimic Vim by importing vim module by default. | |
47 | exec('import vim', self.module.__dict__) | |
46 | 48 | # Handle DirChanged. #296 |
47 | 49 | nvim.command( |
48 | 50 | 'au DirChanged * call rpcnotify({}, "python_chdir", v:event.cwd)' |
63 | 65 | forwarded to Nvim. |
64 | 66 | """ |
65 | 67 | self.nvim = nvim |
66 | info('install import hook/path') | |
68 | pass # replaces next logging statement | |
69 | #info('install import hook/path') | |
67 | 70 | self.hook = path_hook(nvim) |
68 | 71 | sys.path_hooks.append(self.hook) |
69 | 72 | nvim.VIM_SPECIAL_PATH = '_vim_path_' |
70 | 73 | sys.path.append(nvim.VIM_SPECIAL_PATH) |
71 | info('redirect sys.stdout and sys.stderr') | |
74 | pass # replaces next logging statement | |
75 | #info('redirect sys.stdout and sys.stderr') | |
72 | 76 | self.saved_stdout = sys.stdout |
73 | 77 | self.saved_stderr = sys.stderr |
74 | 78 | sys.stdout = RedirectStream(lambda data: nvim.out_write(data)) |
77 | 81 | def teardown(self): |
78 | 82 | """Restore state modified from the `setup` call.""" |
79 | 83 | nvim = self.nvim |
80 | info('uninstall import hook/path') | |
84 | pass # replaces next logging statement | |
85 | #info('uninstall import hook/path') | |
81 | 86 | sys.path.remove(nvim.VIM_SPECIAL_PATH) |
82 | 87 | sys.path_hooks.remove(self.hook) |
83 | info('restore sys.stdout and sys.stderr') | |
88 | pass # replaces next logging statement | |
89 | #info('restore sys.stdout and sys.stderr') | |
84 | 90 | sys.stdout = self.saved_stdout |
85 | 91 | sys.stderr = self.saved_stderr |
86 | 92 |
38 | 38 | return (name, VERSION.__dict__, type_, method_spec, attributes) |
39 | 39 | |
40 | 40 | |
41 | VERSION = Version(major=0, minor=4, patch=0, prerelease='') | |
41 | VERSION = Version(major=0, minor=4, patch=2, prerelease='') |
0 | 0 | Metadata-Version: 2.1 |
1 | 1 | Name: pynvim |
2 | Version: 0.4.0 | |
2 | Version: 0.4.2 | |
3 | 3 | Summary: Python client to neovim |
4 | Home-page: http://github.com/neovim/python-client | |
4 | Home-page: http://github.com/neovim/pynvim | |
5 | 5 | Author: Thiago de Arruda |
6 | 6 | Author-email: tpadilha84@gmail.com |
7 | 7 | License: Apache |
8 | Download-URL: https://github.com/neovim/python-client/archive/0.4.0.tar.gz | |
8 | Download-URL: https://github.com/neovim/pynvim/archive/0.4.2.tar.gz | |
9 | 9 | Description: UNKNOWN |
10 | 10 | Platform: UNKNOWN |
11 | 11 | Provides-Extra: pyuv |
7 | 7 | 'msgpack>=0.5.0', |
8 | 8 | ] |
9 | 9 | |
10 | needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) | |
11 | pytest_runner = ['pytest-runner'] if needs_pytest else [] | |
12 | ||
10 | 13 | setup_requires = [ |
11 | 'pytest-runner' | |
12 | ] | |
14 | ] + pytest_runner, | |
13 | 15 | |
14 | 16 | tests_require = [ |
15 | 17 | 'pytest>=3.4.0', |
32 | 34 | install_requires.append('greenlet') |
33 | 35 | |
34 | 36 | setup(name='pynvim', |
35 | version='0.4.0', | |
37 | version='0.4.2', | |
36 | 38 | description='Python client to neovim', |
37 | url='http://github.com/neovim/python-client', | |
38 | download_url='https://github.com/neovim/python-client/archive/0.4.0.tar.gz', | |
39 | url='http://github.com/neovim/pynvim', | |
40 | download_url='https://github.com/neovim/pynvim/archive/0.4.2.tar.gz', | |
39 | 41 | author='Thiago de Arruda', |
40 | 42 | author_email='tpadilha84@gmail.com', |
41 | 43 | license='Apache', |
17 | 17 | if child_argv is not None: |
18 | 18 | editor = pynvim.attach('child', argv=json.loads(child_argv)) |
19 | 19 | else: |
20 | assert listen_address is None or listen_address != '' | |
20 | 21 | editor = pynvim.attach('socket', path=listen_address) |
21 | 22 | |
22 | 23 | return editor |
163 | 163 | def test_invalid_utf8(vim): |
164 | 164 | vim.command('normal "=printf("%c", 0xFF)\np') |
165 | 165 | assert vim.eval("char2nr(getline(1))") == 0xFF |
166 | ||
167 | 166 | assert vim.current.buffer[:] == ['\udcff'] if IS_PYTHON3 else ['\xff'] |
167 | ||
168 | 168 | vim.current.line += 'x' |
169 | assert vim.eval("getline(1)", decode=False) == b'\xFFx' | |
169 | assert vim.eval("getline(1)", decode=False) == '\udcffx' if IS_PYTHON3 else '\xffx' | |
170 | 170 | assert vim.current.buffer[:] == ['\udcffx'] if IS_PYTHON3 else ['\xffx'] |
171 | 171 | |
172 | 172 |
27 | 27 | assert vim.eval('g:data') == 'xyz' |
28 | 28 | |
29 | 29 | |
30 | def test_async_error(vim): | |
31 | # Invoke a bogus Ex command via notify (async). | |
32 | vim.command("lolwut", async_=True) | |
33 | event = vim.next_message() | |
34 | assert event[1] == 'nvim_error_event' | |
35 | ||
36 | ||
30 | 37 | def test_broadcast(vim): |
31 | 38 | vim.subscribe('event2') |
32 | 39 | vim.command('call rpcnotify(0, "event1", 1, 2, 3)') |
0 | 0 | # -*- coding: utf-8 -*- |
1 | from pynvim.plugin.host import Host, host_method_spec | |
2 | from pynvim.plugin.script_host import ScriptHost | |
1 | 3 | |
2 | from pynvim.plugin.host import Host, host_method_spec | |
4 | ||
5 | def test_host_imports(vim): | |
6 | h = ScriptHost(vim) | |
7 | assert h.module.__dict__['vim'] | |
8 | assert h.module.__dict__['vim'] == h.legacy_vim | |
9 | assert h.module.__dict__['sys'] | |
3 | 10 | |
4 | 11 | |
5 | 12 | def test_host_clientinfo(vim): |
8 | 15 | assert 'remote' == vim.api.get_chan_info(vim.channel_id)['client']['type'] |
9 | 16 | h._load([]) |
10 | 17 | assert 'host' == vim.api.get_chan_info(vim.channel_id)['client']['type'] |
18 | ||
19 | ||
20 | # Smoke test for Host._on_error_event(). #425 | |
21 | def test_host_async_error(vim): | |
22 | h = Host(vim) | |
23 | h._load([]) | |
24 | # Invoke a bogus Ex command via notify (async). | |
25 | vim.command("lolwut", async_=True) | |
26 | event = vim.next_message() | |
27 | assert event[1] == 'nvim_error_event' | |
28 | assert 'rplugin-host: Async request caused an error:\nboom\n' \ | |
29 | in h._on_error_event(None, 'boom') |
225 | 225 | return a.nvim_buf_line_count(buf) |
226 | 226 | end |
227 | 227 | |
228 | pynvimtest = {setbuf=setbuf,getbuf=getbuf} | |
228 | pynvimtest = {setbuf=setbuf, getbuf=getbuf} | |
229 | 229 | |
230 | 230 | return "eggspam" |
231 | 231 | """ |
234 | 234 | def test_lua(vim): |
235 | 235 | assert vim.exec_lua(lua_code, 7) == "eggspam" |
236 | 236 | assert vim.lua.pynvimtest_func(3) == 10 |
237 | testmod = vim.lua.pynvimtest | |
237 | lua_module = vim.lua.pynvimtest | |
238 | 238 | buf = vim.current.buffer |
239 | testmod.setbuf(buf, ["a", "b", "c", "d"], async_=True) | |
240 | assert testmod.getbuf(buf) == 4 | |
239 | lua_module.setbuf(buf, ["a", "b", "c", "d"], async_=True) | |
240 | assert lua_module.getbuf(buf) == 4 |