Skip to content

Commit 1e49430

Browse files
authored
Merge pull request #211 from bfredl/newerr
plugin host: hide irrelevant parts of request tracebacks
2 parents 496f555 + deecb3e commit 1e49430

File tree

4 files changed

+57
-28
lines changed

4 files changed

+57
-28
lines changed

neovim/api/nvim.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import sys
55

6-
from traceback import format_exc, format_stack
6+
from traceback import format_stack
77

88
from msgpack import ExtType
99

@@ -13,7 +13,7 @@
1313
from .tabpage import Tabpage
1414
from .window import Window
1515
from ..compat import IS_PYTHON3
16-
16+
from ..util import format_exc_skip
1717

1818
__all__ = ('Nvim')
1919

@@ -325,7 +325,7 @@ def handler():
325325
except Exception as err:
326326
msg = ("error caught while executing async callback:\n"
327327
"{0!r}\n{1}\n \nthe call was requested at\n{2}"
328-
.format(err, format_exc(5), call_point))
328+
.format(err, format_exc_skip(1, 5), call_point))
329329
self._err_cb(msg)
330330
raise
331331
self._session.threadsafe_call(handler)

neovim/plugin/host.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ..api import decode_if_bytes, walk
1414
from ..compat import IS_PYTHON3, find_module
1515
from ..msgpack_rpc import ErrorResponse
16+
from ..util import format_exc_skip
1617

1718
__all__ = ('Host')
1819

@@ -60,6 +61,24 @@ def shutdown(self):
6061
self._unload()
6162
self.nvim.stop_loop()
6263

64+
def _wrap_function(self, fn, sync, decode, nvim_bind, name, *args):
65+
if decode:
66+
args = walk(decode_if_bytes, args, decode)
67+
if nvim_bind is not None:
68+
args.insert(0, nvim_bind)
69+
try:
70+
return fn(*args)
71+
except Exception:
72+
if sync:
73+
msg = ("error caught in request handler '{} {}':\n{}"
74+
.format(name, args, format_exc_skip(1, 5)))
75+
raise ErrorResponse(msg)
76+
else:
77+
msg = ("error caught in async handler '{} {}'\n{}\n"
78+
.format(name, args, format_exc_skip(1, 5)))
79+
self._on_async_err(msg + "\n")
80+
raise
81+
6382
def _on_request(self, name, args):
6483
"""Handle a msgpack-rpc request."""
6584
if IS_PYTHON3:
@@ -87,13 +106,7 @@ def _on_notification(self, name, args):
87106
return
88107

89108
debug('calling notification handler for "%s", args: "%s"', name, args)
90-
try:
91-
handler(*args)
92-
except Exception as err:
93-
msg = ("error caught in async handler '{} {}':\n{!r}\n{}\n"
94-
.format(name, args, err, format_exc(5)))
95-
self._on_async_err(msg + "\n")
96-
raise
109+
handler(*args)
97110

98111
def _missing_handler_error(self, name, kind):
99112
msg = 'no {} handler registered for "{}"'.format(kind, name)
@@ -156,40 +169,36 @@ def _discover_functions(self, obj, handlers, plugin_path):
156169
def predicate(o):
157170
return hasattr(o, '_nvim_rpc_method_name')
158171

159-
def decoder(fn, decode, *args):
160-
return fn(*walk(decode_if_bytes, args, decode))
161172
specs = []
162173
objdecode = getattr(obj, '_nvim_decode', self._decode_default)
163174
for _, fn in inspect.getmembers(obj, predicate):
175+
sync = fn._nvim_rpc_sync
164176
decode = getattr(fn, '_nvim_decode', objdecode)
177+
nvim_bind = None
165178
if fn._nvim_bind:
166-
# bind a nvim instance to the handler
167-
fn2 = functools.partial(fn, self._configure_nvim_for(fn))
168-
# copy _nvim_* attributes from the original function
169-
self._copy_attributes(fn, fn2)
170-
fn = fn2
171-
if decode:
172-
fn2 = functools.partial(decoder, fn, decode)
173-
self._copy_attributes(fn, fn2)
174-
fn = fn2
179+
nvim_bind = self._configure_nvim_for(fn)
175180

176-
# register in the rpc handler dict
177181
method = fn._nvim_rpc_method_name
178182
if fn._nvim_prefix_plugin_path:
179183
method = '{0}:{1}'.format(plugin_path, method)
180-
if fn._nvim_rpc_sync:
184+
185+
fn_wrapped = functools.partial(self._wrap_function, fn,
186+
sync, decode, nvim_bind, method)
187+
self._copy_attributes(fn, fn_wrapped)
188+
# register in the rpc handler dict
189+
if sync:
181190
if method in self._request_handlers:
182191
raise Exception(('Request handler for "{0}" is ' +
183192
'already registered').format(method))
184-
self._request_handlers[method] = fn
193+
self._request_handlers[method] = fn_wrapped
185194
else:
186195
if method in self._notification_handlers:
187196
raise Exception(('Notification handler for "{0}" is ' +
188197
'already registered').format(method))
189-
self._notification_handlers[method] = fn
198+
self._notification_handlers[method] = fn_wrapped
190199
if hasattr(fn, '_nvim_rpc_spec'):
191200
specs.append(fn._nvim_rpc_spec)
192-
handlers.append(fn)
201+
handlers.append(fn_wrapped)
193202
if specs:
194203
self._specs[plugin_path] = specs
195204

neovim/plugin/script_host.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
from .decorators import plugin, rpc_export
99
from ..api import Nvim, walk
10+
from ..msgpack_rpc import ErrorResponse
11+
from ..util import format_exc_skip
1012

1113
__all__ = ('ScriptHost',)
1214

@@ -72,15 +74,21 @@ def teardown(self):
7274
def python_execute(self, script, range_start, range_stop):
7375
"""Handle the `python` ex command."""
7476
self._set_current_range(range_start, range_stop)
75-
exec(script, self.module.__dict__)
77+
try:
78+
exec(script, self.module.__dict__)
79+
except Exception:
80+
raise ErrorResponse(format_exc_skip(1))
7681

7782
@rpc_export('python_execute_file', sync=True)
7883
def python_execute_file(self, file_path, range_start, range_stop):
7984
"""Handle the `pyfile` ex command."""
8085
self._set_current_range(range_start, range_stop)
8186
with open(file_path) as f:
8287
script = compile(f.read(), file_path, 'exec')
83-
exec(script, self.module.__dict__)
88+
try:
89+
exec(script, self.module.__dict__)
90+
except Exception:
91+
raise ErrorResponse(format_exc_skip(1))
8492

8593
@rpc_export('python_do_range', sync=True)
8694
def python_do_range(self, start, stop, code):

neovim/util.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Shared utility functions."""
2+
3+
import sys
4+
from traceback import format_exception
5+
6+
7+
def format_exc_skip(skip, limit=None):
8+
"""Like traceback.format_exc but allow skipping the first frames."""
9+
type, val, tb = sys.exc_info()
10+
for i in range(skip):
11+
tb = tb.tb_next
12+
return ('\n'.join(format_exception(type, val, tb, limit))).rstrip()

0 commit comments

Comments
 (0)