Skip to content

Commit 50384dc

Browse files
committed
refactor(jsonrpc): use concurrent.Futures instead of asyncio.Futures for request
1 parent 5a22bef commit 50384dc

File tree

3 files changed

+25
-86
lines changed

3 files changed

+25
-86
lines changed

packages/jsonrpc2/src/robotcode/jsonrpc2/protocol.py

Lines changed: 19 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from __future__ import annotations
22

33
import asyncio
4+
import concurrent.futures
45
import inspect
56
import json
67
import re
78
import threading
8-
import time
99
import weakref
1010
from abc import ABC, abstractmethod
1111
from collections import OrderedDict
@@ -36,7 +36,6 @@
3636
from robotcode.core.async_tools import (
3737
HasThreaded,
3838
async_event,
39-
create_sub_future,
4039
create_sub_task,
4140
run_coroutine_in_thread,
4241
)
@@ -336,7 +335,7 @@ def get_param_type(self, name: str) -> Optional[Type[Any]]:
336335

337336

338337
class SendedRequestEntry(NamedTuple):
339-
future: asyncio.Future[Any]
338+
future: concurrent.futures.Future[Any]
340339
result_type: Optional[Type[Any]]
341340

342341

@@ -546,9 +545,9 @@ def send_request(
546545
method: str,
547546
params: Optional[Any] = None,
548547
return_type_or_converter: Optional[Type[_TResult]] = None,
549-
) -> asyncio.Future[Optional[_TResult]]:
548+
) -> concurrent.futures.Future[_TResult]:
550549
with self._sended_request_lock:
551-
result: asyncio.Future[Optional[_TResult]] = create_sub_future()
550+
result: concurrent.futures.Future[_TResult] = concurrent.futures.Future()
552551
self._sended_request_count += 1
553552
id = self._sended_request_count
554553

@@ -559,13 +558,13 @@ def send_request(
559558

560559
return result
561560

562-
async def send_request_async(
561+
def send_request_async(
563562
self,
564563
method: str,
565564
params: Optional[Any] = None,
566565
return_type: Optional[Type[_TResult]] = None,
567-
) -> Optional[_TResult]:
568-
return await self.send_request(method, params, return_type)
566+
) -> asyncio.Future[_TResult]:
567+
return asyncio.wrap_future(self.send_request(method, params, return_type))
569568

570569
@__logger.call
571570
def send_notification(self, method: str, params: Any) -> None:
@@ -590,44 +589,17 @@ async def handle_response(self, message: JsonRPCResponse) -> None:
590589

591590
try:
592591
if not entry.future.done():
593-
res = None
594-
if message.result is not None:
595-
res = from_dict(message.result, entry.result_type)
596-
if entry.future.get_loop() == asyncio.get_running_loop():
597-
entry.future.set_result(res)
598-
else:
599-
if entry.future.get_loop().is_running():
600-
601-
def set_result(f: asyncio.Future[Any], r: Any, ev: threading.Event) -> None:
602-
try:
603-
if not f.done() and f.get_loop().is_running():
604-
f.set_result(r)
605-
finally:
606-
ev.set()
607-
608-
done = threading.Event()
609-
610-
entry.future.get_loop().call_soon_threadsafe(set_result, entry.future, res, done)
611-
612-
start = time.monotonic()
613-
while not done.is_set():
614-
if time.monotonic() - start > 120:
615-
raise TimeoutError("Can't set future result.")
616-
617-
await asyncio.sleep(0)
618-
619-
else:
620-
self.__logger.warning(lambda: f"Response {entry!r} loop is not running.")
592+
entry.future.set_result(
593+
from_dict(message.result, entry.result_type) if message.result is not None else None
594+
)
595+
else:
596+
self.__logger.warning(lambda: f"Response for {message} is already done.")
621597

622598
except (SystemExit, KeyboardInterrupt):
623599
raise
624600
except BaseException as e:
625601
if not entry.future.done():
626-
if entry.future.get_loop() == asyncio.get_running_loop():
627-
entry.future.set_exception(e)
628-
else:
629-
if entry.future.get_loop().is_running():
630-
entry.future.get_loop().call_soon_threadsafe(entry.future.set_exception, e)
602+
entry.future.set_exception(e)
631603

632604
@__logger.call
633605
async def handle_error(self, message: JsonRPCError) -> None:
@@ -646,50 +618,17 @@ async def handle_error(self, message: JsonRPCError) -> None:
646618

647619
try:
648620
if not entry.future.done():
649-
res = None
650-
if message.result is not None:
651-
res = from_dict(message.result, entry.result_type)
652-
if entry.future.get_loop() == asyncio.get_running_loop():
653-
entry.future.set_exception(
654-
JsonRPCErrorException(message.error.code, message.error.message, message.error.data)
655-
)
656-
else:
657-
if entry.future.get_loop().is_running():
658-
659-
def set_result(f: asyncio.Future[Any], r: Any, ev: threading.Event) -> None:
660-
try:
661-
if not f.done() and f.get_loop().is_running():
662-
f.set_exception(
663-
JsonRPCErrorException(
664-
message.error.code, message.error.message, message.error.data
665-
)
666-
)
667-
finally:
668-
ev.set()
669-
670-
done = threading.Event()
671-
672-
entry.future.get_loop().call_soon_threadsafe(set_result, entry.future, res, done)
673-
674-
start = time.monotonic()
675-
while not done.is_set():
676-
if time.monotonic() - start > 120:
677-
raise TimeoutError("Can't set future result.")
678-
679-
await asyncio.sleep(0)
680-
681-
else:
682-
self.__logger.warning(lambda: f"Response {entry!r} loop is not running.")
621+
entry.future.set_exception(
622+
JsonRPCErrorException(message.error.code, message.error.message, message.error.data)
623+
)
624+
else:
625+
self.__logger.warning(lambda: f"Response for {message} is already done.")
683626

684627
except (SystemExit, KeyboardInterrupt):
685628
raise
686629
except BaseException as e:
687630
if not entry.future.done():
688-
if entry.future.get_loop() == asyncio.get_running_loop():
689-
entry.future.set_exception(e)
690-
else:
691-
if entry.future.get_loop().is_running():
692-
entry.future.get_loop().call_soon_threadsafe(entry.future.set_exception, e)
631+
entry.future.set_exception(e)
693632

694633
@staticmethod
695634
def _convert_params(

packages/language_server/src/robotcode/language_server/common/parts/code_lens.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,4 @@ async def __refresh(self) -> None:
109109
):
110110
return
111111

112-
await self.parent.send_request("workspace/codeLens/refresh")
112+
await self.parent.send_request_async("workspace/codeLens/refresh")

tests/robotcode/jsonrpc/test_jsonrpcprotocol.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ async def test_receive_response_should_work() -> None:
143143
data = header + json_message
144144
await protocol.data_received_async(data)
145145

146-
a = await asyncio.wait_for(r, 10)
146+
a = r.result(10)
147147

148148
assert a == ["dummy", "data"]
149149

@@ -176,7 +176,7 @@ async def test_send_request_receive_response_should_work_without_param_type_work
176176
data = header + json_message
177177
await protocol.data_received_async(data)
178178

179-
a = await asyncio.wait_for(r, 10)
179+
a = r.result(10)
180180

181181
assert isinstance(a, dict)
182182
assert a == {"title": "hi there"}
@@ -196,7 +196,7 @@ async def test_receive_response_should_work_with_dataclass() -> None:
196196
data = header + json_message
197197
await protocol.data_received_async(data)
198198

199-
a = await asyncio.wait_for(r, 10)
199+
a = r.result(10)
200200

201201
assert a == MessageActionItem(title="hi there")
202202

@@ -215,7 +215,7 @@ async def test_receive_response_should_work_with_generic_list() -> None:
215215
data = header + json_message
216216
await protocol.data_received_async(data)
217217

218-
a = await asyncio.wait_for(r, 10)
218+
a = r.result(10)
219219

220220
assert a == [MessageActionItem(title="hi there")]
221221

@@ -234,6 +234,6 @@ async def test_receive_response_with_generic_dict_should_return_unchanged() -> N
234234
data = header + json_message
235235
await protocol.data_received_async(data)
236236

237-
a = await asyncio.wait_for(r, 10)
237+
a = r.result(10)
238238

239239
assert a == [as_dict(MessageActionItem(title="hi there"))]

0 commit comments

Comments
 (0)