Skip to content

Commit 4be9207

Browse files
committed
better handler of Exceptions in async contexts
1 parent 956f030 commit 4be9207

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

neovim/api/nvim.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import functools
33
import os
44

5+
from traceback import format_exc, format_stack
6+
57
from msgpack import ExtType
68

79
from .buffer import Buffer
@@ -204,9 +206,9 @@ def out_write(self, msg):
204206
"""Print `msg` as a normal message."""
205207
return self._session.request('vim_out_write', msg)
206208

207-
def err_write(self, msg):
209+
def err_write(self, msg, async=False):
208210
"""Print `msg` as an error message."""
209-
return self._session.request('vim_err_write', msg)
211+
return self._session.request('vim_err_write', msg, async=async)
210212

211213
def quit(self, quit_command='qa!'):
212214
"""Send a quit command to Nvim.
@@ -226,6 +228,29 @@ def new_highlight_source(self):
226228
"""Return new src_id for use with Buffer.add_highlight."""
227229
return self.current.buffer.add_highlight("", 0, src_id=0)
228230

231+
def async_call(self, fn, *args, **kwargs):
232+
"""Schedule `fn` to be called by the event loop soon.
233+
234+
This function is thread-safe, and is the only way code not
235+
on the main thread could interact with nvim api objects.
236+
237+
This function can also be called in a synchronous
238+
event handler, just before it returns, to defer execution
239+
that shouldn't block neovim.
240+
"""
241+
call_point = ''.join(format_stack(None, 5)[:-1])
242+
243+
def handler():
244+
try:
245+
fn(*args, **kwargs)
246+
except Exception as err:
247+
msg = ("error caught while executing async callback:\n"
248+
"{!r}\n{}\n \nthe call was requested at\n{}"
249+
.format(err, format_exc(5), call_point))
250+
self.err_write(msg, async=True)
251+
raise
252+
self._session.threadsafe_call(handler)
253+
229254

230255
class Current(object):
231256

neovim/msgpack_rpc/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ def __init__(self, async_session):
3131
def threadsafe_call(self, fn, *args, **kwargs):
3232
"""Wrapper around `AsyncSession.threadsafe_call`."""
3333
def handler():
34-
fn(*args, **kwargs)
34+
try:
35+
fn(*args, **kwargs)
36+
except Exception:
37+
warn("error caught while excecuting async callback\n%s\n",
38+
format_exc())
3539

3640
def greenlet_wrapper():
3741
gr = greenlet.greenlet(handler)
@@ -197,6 +201,7 @@ def handler():
197201
except Exception:
198202
warn("error caught while processing notification '%s %s': %s",
199203
name, args, format_exc())
204+
200205
debug('greenlet %s is now dying...', gr)
201206

202207
gr = greenlet.greenlet(handler)

neovim/plugin/host.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
import os
77
import os.path
88

9+
from traceback import format_exc
10+
911
from ..api import DecodeHook
1012
from ..compat import IS_PYTHON3, find_module
1113

12-
1314
__all__ = ('Host')
1415

1516
logger = logging.getLogger(__name__)
@@ -76,7 +77,13 @@ def _on_notification(self, name, args):
7677
return
7778

7879
debug('calling notification handler for "%s", args: "%s"', name, args)
79-
handler(*args)
80+
try:
81+
handler(*args)
82+
except Exception as err:
83+
msg = ("error caught in async handler '{} {}':\n{!r}\n{}"
84+
.format(name, args, err, format_exc(5)))
85+
self.nvim.err_write(msg, async=True)
86+
raise
8087

8188
def _load(self, plugins):
8289
for path in plugins:

0 commit comments

Comments
 (0)