Skip to content

Commit 977831a

Browse files
committed
catch requests from invalid thread
1 parent 0c5257a commit 977831a

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

neovim/api/nvim.py

+14
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,6 +392,10 @@ def out_write(self, msg, **kwargs):
382392

383393
def err_write(self, msg, **kwargs):
384394
"""Print `msg` as an error message."""
395+
if (self._session._loop_thread is not None and
396+
threading.current_thread() != self._session._loop_thread):
397+
self.async_call(self.err_write, msg, **kwargs)
398+
return
385399
return self.request('nvim_err_write', msg, **kwargs)
386400

387401
def quit(self, quit_command='qa!'):

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

0 commit comments

Comments
 (0)