Skip to content

Commit 34e40ae

Browse files
committed
async_call: only use err_write when called from plugin
1 parent 3ed0e93 commit 34e40ae

File tree

3 files changed

+31
-17
lines changed

3 files changed

+31
-17
lines changed

neovim/api/nvim.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Main Nvim interface."""
22
import functools
33
import os
4+
import sys
45

56
from traceback import format_exc, format_stack
67

@@ -65,9 +66,10 @@ def from_session(cls, session):
6566
def from_nvim(cls, nvim):
6667
"""Create a new Nvim instance from an existing instance."""
6768
return cls(nvim._session, nvim.channel_id, nvim.metadata,
68-
nvim.types, nvim._decodehook)
69+
nvim.types, nvim._decodehook, nvim._err_cb)
6970

70-
def __init__(self, session, channel_id, metadata, types, decodehook=None):
71+
def __init__(self, session, channel_id, metadata, types,
72+
decodehook=None, err_cb=None):
7173
"""Initialize a new Nvim instance. This method is module-private."""
7274
self._session = session
7375
self.channel_id = channel_id
@@ -84,6 +86,7 @@ def __init__(self, session, channel_id, metadata, types, decodehook=None):
8486
self.funcs = Funcs(self)
8587
self.error = NvimError
8688
self._decodehook = decodehook
89+
self._err_cb = err_cb
8790

8891
def _from_nvim(self, obj):
8992
if type(obj) is ExtType:
@@ -132,7 +135,8 @@ def next_message(self):
132135
if msg:
133136
return walk(self._from_nvim, msg)
134137

135-
def run_loop(self, request_cb, notification_cb, setup_cb=None):
138+
def run_loop(self, request_cb, notification_cb,
139+
setup_cb=None, err_cb=None):
136140
"""Run the event loop to receive requests and notifications from Nvim.
137141
138142
This should not be called from a plugin running in the host, which
@@ -146,6 +150,10 @@ def filter_request_cb(name, args):
146150
def filter_notification_cb(name, args):
147151
notification_cb(self._from_nvim(name), walk(self._from_nvim, args))
148152

153+
if err_cb is None:
154+
err_cb = sys.stderr.write
155+
self._err_cb = err_cb
156+
149157
self._session.run(filter_request_cb, filter_notification_cb, setup_cb)
150158

151159
def stop_loop(self):
@@ -155,7 +163,7 @@ def stop_loop(self):
155163
def with_decodehook(self, hook):
156164
"""Initialize a new Nvim instance."""
157165
return Nvim(self._session, self.channel_id,
158-
self.metadata, self.types, hook)
166+
self.metadata, self.types, hook, self._err_cb)
159167

160168
def ui_attach(self, width, height, rgb):
161169
"""Register as a remote UI.
@@ -316,9 +324,9 @@ def handler():
316324
fn(*args, **kwargs)
317325
except Exception as err:
318326
msg = ("error caught while executing async callback:\n"
319-
"{!r}\n{}\n \nthe call was requested at\n{}"
327+
"{0!r}\n{1}\n \nthe call was requested at\n{2}"
320328
.format(err, format_exc(5), call_point))
321-
self.err_write(msg, async=True)
329+
self._err_cb(msg)
322330
raise
323331
self._session.threadsafe_call(handler)
324332

neovim/plugin/host.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@ def __init__(self, nvim):
4545
if IS_PYTHON3 and isinstance(self._nvim_encoding, bytes):
4646
self._nvim_encoding = self._nvim_encoding.decode('ascii')
4747

48+
def _on_async_err(self, msg):
49+
self.nvim.err_write(msg, async=True)
50+
4851
def start(self, plugins):
4952
"""Start listening for msgpack-rpc requests and notifications."""
5053
self.nvim.run_loop(self._on_request,
5154
self._on_notification,
52-
lambda: self._load(plugins))
55+
lambda: self._load(plugins),
56+
err_cb=self._on_async_err)
5357

5458
def shutdown(self):
5559
"""Shutdown the host."""
@@ -79,16 +83,16 @@ def _on_notification(self, name, args):
7983
if not handler:
8084
msg = self._missing_handler_error(name, 'notification')
8185
error(msg)
82-
self.nvim.err_write(msg + "\n")
86+
self._on_async_err(msg + "\n")
8387
return
8488

8589
debug('calling notification handler for "%s", args: "%s"', name, args)
8690
try:
8791
handler(*args)
8892
except Exception as err:
89-
msg = ("error caught in async handler '{} {}':\n{!r}\n{}"
93+
msg = ("error caught in async handler '{} {}':\n{!r}\n{}\n"
9094
.format(name, args, err, format_exc(5)))
91-
self.nvim.err_write(msg, async=True)
95+
self._on_async_err(msg + "\n")
9296
raise
9397

9498
def _missing_handler_error(self, name, kind):

test/test_concurrency.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
from test_common import vim, cleanup
33
from threading import Timer
44

5-
65
@with_setup(setup=cleanup)
76
def test_interrupt_from_another_thread():
8-
session = vim.session
9-
timer = Timer(0.5, lambda: vim.async_call(lambda: session.stop()))
7+
timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop()))
108
timer.start()
119
eq(vim.next_message(), None)
1210

13-
1411
@with_setup(setup=cleanup)
1512
def test_exception_in_threadsafe_call():
1613
# an exception in a threadsafe_call shouldn't crash the entire host
17-
vim.session.threadsafe_call(lambda: [vim.eval("3"), undefined_variable])
18-
vim.session.threadsafe_call(lambda: vim.stop_loop())
19-
vim.run_loop(None, None, lambda: vim.input("<cr>"))
14+
msgs = []
15+
vim.async_call(lambda: [vim.eval("3"), undefined_variable])
16+
timer = Timer(0.5, lambda: vim.async_call(lambda: vim.stop_loop()))
17+
timer.start()
18+
vim.run_loop(None, None, err_cb=msgs.append)
19+
eq(len(msgs), 1)
20+
msgs[0].index('NameError')
21+
msgs[0].index('undefined_variable')

0 commit comments

Comments
 (0)