Skip to content

Commit c7025e7

Browse files
committed
Merge pull request #178 from bfredl/urm_err
be more verbose about import errors
2 parents 2d9fb26 + c960e32 commit c7025e7

File tree

4 files changed

+48
-14
lines changed

4 files changed

+48
-14
lines changed

neovim/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77
import sys
88

99
from .api import DecodeHook, Nvim, SessionHook
10-
from .msgpack_rpc import (child_session, socket_session, stdio_session,
11-
tcp_session)
10+
from .msgpack_rpc import (ErrorResponse, child_session, socket_session,
11+
stdio_session, tcp_session)
1212
from .plugin import (Host, autocmd, command, encoding, function, plugin,
1313
rpc_export, shutdown_hook)
1414

1515

1616
__all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
1717
'start_host', 'autocmd', 'command', 'encoding', 'function',
1818
'plugin', 'rpc_export', 'Host', 'DecodeHook', 'Nvim',
19-
'SessionHook', 'shutdown_hook', 'attach', 'setup_logging')
19+
'SessionHook', 'shutdown_hook', 'attach', 'setup_logging',
20+
'ErrorResponse')
2021

2122

2223
def start_host(session=None):

neovim/msgpack_rpc/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
from .async_session import AsyncSession
88
from .event_loop import EventLoop
99
from .msgpack_stream import MsgpackStream
10-
from .session import Session
10+
from .session import ErrorResponse, Session
1111

1212

13-
__all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session')
13+
__all__ = ('tcp_session', 'socket_session', 'stdio_session', 'child_session',
14+
'ErrorResponse')
1415

1516

1617
def session(transport_type='stdio', *args, **kwargs):

neovim/msgpack_rpc/session.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ def handler():
182182
debug('greenlet %s finished executing, ' +
183183
'sending %s as response', gr, rv)
184184
response.send(rv)
185+
except ErrorResponse as err:
186+
warn("error response from request '%s %s': %s", name,
187+
args, format_exc())
188+
response.send(err.args[0], error=True)
185189
except Exception as err:
186190
warn("error caught while processing request '%s %s': %s", name,
187191
args, format_exc())
@@ -207,3 +211,15 @@ def handler():
207211
gr = greenlet.greenlet(handler)
208212
debug('received rpc notification, greenlet %s will handle it', gr)
209213
gr.switch()
214+
215+
216+
class ErrorResponse(BaseException):
217+
218+
"""Raise this in a request handler to respond with a given error message.
219+
220+
Unlike when other exceptions are caught, this gives full control off the
221+
error response sent. When "ErrorResponse(msg)" is caught "msg" will be
222+
sent verbatim as the error response.No traceback will be appended.
223+
"""
224+
225+
pass

neovim/plugin/host.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import logging
66
import os
77
import os.path
8+
import re
89

910
from traceback import format_exc
1011

1112
from ..api import DecodeHook
1213
from ..compat import IS_PYTHON3, find_module
14+
from ..msgpack_rpc import ErrorResponse
1315

1416
__all__ = ('Host')
1517

@@ -31,6 +33,7 @@ def __init__(self, nvim):
3133
self.nvim = nvim
3234
self._specs = {}
3335
self._loaded = {}
36+
self._load_errors = {}
3437
self._notification_handlers = {}
3538
self._request_handlers = {
3639
'poll': lambda: 'ok',
@@ -58,9 +61,9 @@ def _on_request(self, name, args):
5861
name = name.decode(self._nvim_encoding)
5962
handler = self._request_handlers.get(name, None)
6063
if not handler:
61-
msg = 'no request handler registered for "%s"' % name
62-
warn(msg)
63-
raise Exception(msg)
64+
msg = self._missing_handler_error(name, 'request')
65+
error(msg)
66+
raise ErrorResponse(msg)
6467

6568
debug('calling request handler for "%s", args: "%s"', name, args)
6669
rv = handler(*args)
@@ -73,7 +76,9 @@ def _on_notification(self, name, args):
7376
name = name.decode(self._nvim_encoding)
7477
handler = self._notification_handlers.get(name, None)
7578
if not handler:
76-
warn('no notification handler registered for "%s"', name)
79+
msg = self._missing_handler_error(name, 'notification')
80+
error(msg)
81+
self.nvim.err_write(msg + "\n")
7782
return
7883

7984
debug('calling notification handler for "%s", args: "%s"', name, args)
@@ -85,8 +90,18 @@ def _on_notification(self, name, args):
8590
self.nvim.err_write(msg, async=True)
8691
raise
8792

93+
def _missing_handler_error(self, name, kind):
94+
msg = 'no {} handler registered for "{}"'.format(kind, name)
95+
pathmatch = re.match(r'(.+):[^:]+:[^:]+', name)
96+
if pathmatch:
97+
loader_error = self._load_errors.get(pathmatch.group(1))
98+
if loader_error is not None:
99+
msg = msg + "\n" + loader_error
100+
return msg
101+
88102
def _load(self, plugins):
89103
for path in plugins:
104+
err = None
90105
if path in self._loaded:
91106
error('{0} is already loaded'.format(path))
92107
continue
@@ -105,12 +120,11 @@ def _load(self, plugins):
105120
error('{0} exports no handlers'.format(path))
106121
continue
107122
self._loaded[path] = {'handlers': handlers, 'module': module}
108-
except (ImportError, SyntaxError) as e:
109-
error(('Encountered import error loading '
110-
'plugin at {0}: {1}').format(path, e))
111123
except Exception as e:
112-
error('Error loading plugin at {0} {1}: {2}'.format(
113-
path, type(e).__name__, e))
124+
err = ('Encountered {} loading plugin at {}: {}\n{}'
125+
.format(type(e).__name__, path, e, format_exc(5)))
126+
error(err)
127+
self._load_errors[path] = err
114128

115129
def _unload(self):
116130
for path, plugin in self._loaded.items():
@@ -183,6 +197,8 @@ def _copy_attributes(self, fn, fn2):
183197
def _on_specs_request(self, path):
184198
if IS_PYTHON3 and isinstance(path, bytes):
185199
path = path.decode(self._nvim_encoding)
200+
if path in self._load_errors:
201+
self.nvim.out_write(self._load_errors[path] + '\n')
186202
return self._specs.get(path, 0)
187203

188204
def _decodehook_for(self, encoding):

0 commit comments

Comments
 (0)