Skip to content

Commit 0852da8

Browse files
authored
Merge pull request #255 from bfredl/threading
catch requests from invalid thread
2 parents 0c5257a + d2bf46f commit 0852da8

File tree

3 files changed

+26
-0
lines changed

3 files changed

+26
-0
lines changed

neovim/api/nvim.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Main Nvim interface."""
22
import os
33
import sys
4+
import threading
45
from functools import partial
56
from traceback import format_stack
67

@@ -165,6 +166,15 @@ def request(self, name, *args, **kwargs):
165166
present and True, a asynchronous notification is sent instead. This
166167
will never block, and the return value or error is ignored.
167168
"""
169+
if (self._session._loop_thread is not None and
170+
threading.current_thread() != self._session._loop_thread):
171+
172+
msg = ("request from non-main thread:\n{}\n"
173+
.format('\n'.join(format_stack(None, 5)[:-1])))
174+
175+
self.async_call(self._err_cb, msg)
176+
raise NvimError("request from non-main thread")
177+
168178
decode = kwargs.pop('decode', self._decode)
169179
args = walk(self._to_nvim, args)
170180
res = self._session.request(name, *args, **kwargs)
@@ -382,8 +392,18 @@ def out_write(self, msg, **kwargs):
382392

383393
def err_write(self, msg, **kwargs):
384394
"""Print `msg` as an error message."""
395+
if self._thread_invalid():
396+
# special case: if a non-main thread writes to stderr
397+
# i.e. due to an uncaught exception, pass it through
398+
# without raising an additional exception.
399+
self.async_call(self.err_write, msg, **kwargs)
400+
return
385401
return self.request('nvim_err_write', msg, **kwargs)
386402

403+
def _thread_invalid(self):
404+
return (self._session._loop_thread is not None and
405+
threading.current_thread() != self._session._loop_thread)
406+
387407
def quit(self, quit_command='qa!'):
388408
"""Send a quit command to Nvim.
389409

neovim/msgpack_rpc/session.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Synchronous msgpack-rpc session layer."""
22
import logging
3+
import threading
34
from collections import deque
45
from traceback import format_exc
56

@@ -29,6 +30,7 @@ def __init__(self, async_session):
2930
self._is_running = False
3031
self._setup_exception = None
3132
self.loop = async_session.loop
33+
self._loop_thread = None
3234

3335
def threadsafe_call(self, fn, *args, **kwargs):
3436
"""Wrapper around `AsyncSession.threadsafe_call`."""
@@ -110,6 +112,7 @@ def run(self, request_cb, notification_cb, setup_cb=None):
110112
self._notification_cb = notification_cb
111113
self._is_running = True
112114
self._setup_exception = None
115+
self._loop_thread = threading.current_thread()
113116

114117
def on_setup():
115118
try:
@@ -135,6 +138,7 @@ def on_setup():
135138
self._is_running = False
136139
self._request_cb = None
137140
self._notification_cb = None
141+
self._loop_thread = None
138142

139143
if self._setup_exception:
140144
raise self._setup_exception

neovim/plugin/script_host.py

+2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ def eval(self, expr):
208208
# This was copied/adapted from nvim-python help
209209
def path_hook(nvim):
210210
def _get_paths():
211+
if nvim._thread_invalid():
212+
return []
211213
return discover_runtime_directories(nvim)
212214

213215
def _find_module(fullname, oldtail, path):

0 commit comments

Comments
 (0)