Skip to content

Commit 35eaa1f

Browse files
committed
do stdio read/write in threadpool
1 parent 371eedf commit 35eaa1f

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

atest/01_Editor.robot

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ CSS
1919

2020
Docker
2121
${def} = Set Variable xpath://span[contains(@class, 'cm-string')][contains(text(), 'PLANET')]
22-
Wait Until Keyword Succeeds 3x 100ms Editor Shows Features for Language Docker Dockerfile Diagnostics=Instruction has no arguments Jump to Definition=${def} Rename=${def}
22+
Wait Until Keyword Succeeds 3x 100ms Editor Shows Features for Language Docker Dockerfile Diagnostics=Instruction has no arguments
23+
... Jump to Definition=${def} Rename=${def}
2324

2425
JS
2526
${def} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), 'fib')])[last()]

py_src/jupyter_lsp/stdio.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
import asyncio
1212
import io
1313
import os
14+
from concurrent.futures import ThreadPoolExecutor
1415
from typing import Text
1516

17+
from tornado.concurrent import run_on_executor
18+
from tornado.gen import convert_yielded
1619
from tornado.httputil import HTTPHeaders
17-
from tornado.queues import Queue
1820
from tornado.ioloop import IOLoop
21+
from tornado.queues import Queue
1922
from traitlets import Float, Instance, default
2023
from traitlets.config import LoggingConfigurable
2124

@@ -26,6 +29,8 @@ class LspStdIoBase(LoggingConfigurable):
2629
""" Non-blocking, queued base for communicating with stdio Language Servers
2730
"""
2831

32+
executor = None
33+
2934
stream = Instance(io.BufferedIOBase, help="the stream to read/write")
3035
queue = Instance(Queue, help="queue to get/put")
3136

@@ -35,6 +40,7 @@ def __repr__(self): # pragma: no cover
3540
def __init__(self, **kwargs):
3641
super().__init__(**kwargs)
3742
self.log.debug("%s initialized", self)
43+
self.executor = ThreadPoolExecutor(max_workers=1)
3844

3945
def close(self):
4046
self.stream.close()
@@ -99,12 +105,12 @@ async def read_one(self) -> Text:
99105
message = ""
100106
headers = HTTPHeaders()
101107

102-
line = self._readline()
108+
line = await convert_yielded(self._readline())
103109

104110
if line:
105111
while line and line.strip():
106112
headers.parse_line(line)
107-
line = self._readline()
113+
line = await convert_yielded(self._readline())
108114

109115
content_length = int(headers.get("content-length", "0"))
110116

@@ -130,6 +136,7 @@ async def read_one(self) -> Text:
130136

131137
return message
132138

139+
@run_on_executor
133140
def _readline(self) -> Text:
134141
""" Read a line (or immediately return None)
135142
"""
@@ -143,17 +150,21 @@ class LspStdIoWriter(LspStdIoBase):
143150
""" Language Server stdio Writer
144151
"""
145152

146-
async def write(self):
153+
async def write(self) -> None:
147154
""" Write to a Language Server until it closes
148155
"""
149156
while not self.stream.closed:
150157
message = await self.queue.get()
151158
try:
152159
body = message.encode("utf-8")
153160
response = "Content-Length: {}\r\n\r\n{}".format(len(body), message)
154-
self.stream.write(response.encode("utf-8"))
155-
self.stream.flush()
161+
await convert_yielded(self._write_one(response.encode("utf-8")))
156162
except Exception: # pragma: no cover
157163
self.log.exception("s couldn't write message: %s", self, response)
158164
finally:
159165
self.queue.task_done()
166+
167+
@run_on_executor
168+
def _write_one(self, message) -> None:
169+
self.stream.write(message)
170+
self.stream.flush()

0 commit comments

Comments
 (0)