44import logging
55import uuid
66import sys
7+ import asyncio
78
9+ from threading import Thread
810from concurrent import futures
911from .exceptions import (JsonRpcException , JsonRpcRequestCancelled ,
1012 JsonRpcInternalError , JsonRpcMethodNotFound )
1416CANCEL_METHOD = '$/cancelRequest'
1517
1618
19+ async def run_as_daemon (func , * args ):
20+ future = futures .Future ()
21+ future .set_running_or_notify_cancel ()
22+
23+ # A bug in python 3.7 makes it a bad idea to set a BaseException
24+ # in a wrapped future (see except statement in asyncio.Task.__wakeup)
25+ # Instead, we'll wrap base exceptions into exceptions and unwrap them
26+ # on the other side of the call.
27+ class BaseExceptionWrapper (Exception ):
28+ pass
29+
30+ def daemon ():
31+ try :
32+ result = func (* args )
33+ except Exception as e :
34+ future .set_exception (e )
35+ except BaseException as e :
36+ future .set_exception (BaseExceptionWrapper (e ))
37+ else :
38+ future .set_result (result )
39+
40+ Thread (target = daemon , daemon = True ).start ()
41+ try :
42+ return await asyncio .wrap_future (future )
43+ except BaseExceptionWrapper as exc :
44+ raise exc .args [0 ]
45+
46+
1747class Endpoint :
1848
1949 def __init__ (self , dispatcher , consumer , id_generator = lambda : str (uuid .uuid4 ()), max_workers = 5 ):
@@ -35,6 +65,19 @@ def __init__(self, dispatcher, consumer, id_generator=lambda: str(uuid.uuid4()),
3565 self ._client_request_futures = {}
3666 self ._server_request_futures = {}
3767 self ._executor_service = futures .ThreadPoolExecutor (max_workers = max_workers )
68+ self ._cancelledRequests = set ()
69+
70+ def init_async (self ):
71+ log .warning ("init async" )
72+ self ._messageQueue = asyncio .Queue ()
73+
74+ async def consume_task (self ):
75+ log .warning ("starting task" )
76+ while True :
77+ message = await self ._messageQueue .get ()
78+ await run_as_daemon (self .consume , message )
79+ log .warning ("got message in task" )
80+ self ._messageQueue .task_done ()
3881
3982 def shutdown (self ):
4083 self ._executor_service .shutdown ()
@@ -94,7 +137,15 @@ def callback(future):
94137 future .set_exception (JsonRpcRequestCancelled ())
95138 return callback
96139
140+ async def consume_async (self , message ):
141+ log .warning ("got message put in queue" )
142+ if message ['method' ] == CANCEL_METHOD :
143+ self ._cancelledRequests .add (message .get ('params' )['id' ])
144+ await self ._messageQueue .put (message )
145+
146+
97147 def consume (self , message ):
148+ log .warning ("consume message" )
98149 """Consume a JSON RPC message from the client.
99150
100151 Args:
@@ -182,6 +233,9 @@ def _handle_request(self, msg_id, method, params):
182233 except KeyError as e :
183234 raise JsonRpcMethodNotFound .of (method ) from e
184235
236+ if msg_id in self ._cancelledRequests :
237+ raise JsonRpcRequestCancelled ()
238+
185239 handler_result = handler (params )
186240
187241 if callable (handler_result ):
0 commit comments