Skip to content

Commit 32b327c

Browse files
committed
speed improvements and big refactoring: introduce *treadsafe* asyncio, to run severals tasks in different threads
1 parent 269c2bc commit 32b327c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1459
-985
lines changed

.vscode/launch.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
{
66
"version": "0.2.0",
77
"configurations": [
8+
{
9+
"name": "Python: Attach using Process Id",
10+
"type": "python",
11+
"request": "attach",
12+
"processId": "${command:pickProcess}"
13+
},
814
{
915
"name": "Python: Current File",
1016
"type": "python",

log.ini

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
[loggers]
2-
#keys: root,server,language_server,language_server_parts,jsonrpc2,jsonrpc2_message,robotframework,debugger, debugger_launcher, asyncio
3-
keys: root,server,language_server,language_server_parts,robotframework,debugger, debugger_launcher, asyncio
2+
keys: root,server,language_server,language_server_parts,jsonrpc2,jsonrpc2_message,robotframework,debugger, debugger_launcher, asyncio, robotcode_utils
3+
#keys: root,server,language_server,language_server_parts,robotframework,debugger, debugger_launcher, asyncio, robotcode_utils
44
#keys: root,server,robotframework,language_server,language_server_parts
5+
#keys: root, asyncio, robotcode_utils
6+
#keys: root, robotcode_utils
7+
#keys: root, diagnostics, document, jsonrpc2, jsonrpc2_message
8+
#keys: root, diagnostics, robot_diagnostics, robocop_diagnostics
9+
#keys: root
510

611
[formatters]
7-
keys: detailed,simple,colored_simple
12+
#keys: detailed,simple,colored_simple
13+
keys: detailed,simple
814

915
[handlers]
10-
#keys: console,colored_console
16+
#keys: console, colored_console
1117
keys: console
1218

1319
[formatter_simple]
@@ -20,15 +26,15 @@ class: coloredlogs.ColoredFormatter
2026
format: %(name)s:%(levelname)s: %(message)s
2127

2228
[formatter_detailed]
23-
format: %(name)s:%(levelname)s %(module)s:%(lineno)d: %(message)s
29+
format: %(name)s:%(levelname)s %(threadName)s(%(thread)d) %(module)s:%(funcName)s: %(message)s (%(pathname)s:%(lineno)d)
2430

2531
[formatter_colored_detailed]
2632
format: %(name)s:%(levelname)s %(module)s:%(lineno)d: %(message)s
2733

2834
[handler_console]
2935
class: StreamHandler
3036
args: []
31-
formatter: simple
37+
formatter: detailed
3238

3339
[handler_colored_console]
3440
class: StreamHandler
@@ -39,7 +45,7 @@ formatter: colored_simple
3945
level: INFO
4046
#handlers: colored_console
4147
handlers: console
42-
propagate: 0
48+
propagate: 1
4349

4450
[logger_server]
4551
level: TRACE
@@ -71,7 +77,7 @@ qualname: robotcode.jsonrpc2
7177
#propagate: 0
7278

7379
[logger_jsonrpc2_message]
74-
level: NOTSET
80+
level: INFO
7581
handlers:
7682
qualname: robotcode.jsonrpc2.protocol.JsonRPCProtocol.message
7783
propagate: 0
@@ -91,3 +97,31 @@ level: TRACE
9197
handlers:
9298
qualname: asyncio
9399

100+
[logger_robotcode_utils]
101+
level: TRACE
102+
handlers:
103+
qualname: robotcode.utils
104+
105+
[logger_diagnostics]
106+
level: TRACE
107+
handlers:
108+
qualname: robotcode.language_server.common.parts.diagnostics
109+
110+
[logger_robot_diagnostics]
111+
level: TRACE
112+
handlers:
113+
qualname: robotcode.language_server.robotframework.parts.diagnostics
114+
115+
116+
[logger_robocop_diagnostics]
117+
level: TRACE
118+
handlers:
119+
qualname: robotcode.language_server.robotframework.parts.robocop_diagnostics
120+
121+
122+
[logger_document]
123+
level: TRACE
124+
handlers:
125+
qualname: robotcode.language_server.common.text_document
126+
127+

poetry.lock

Lines changed: 43 additions & 131 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

robotcode/debugger/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def __init__(self, parent: DebugAdapterProtocol) -> None:
1818
self.exited = False
1919
self.terminated = False
2020

21-
async def handle_event(self, message: Event) -> None:
21+
def handle_event(self, message: Event) -> None:
2222
if message.event == "exited":
2323
self.exited = True
2424
elif message.event == "terminated":

robotcode/debugger/launcher/server.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ async def _launch(
198198
env = {k: ("" if v is None else str(v)) for k, v in env.items()} if env else {}
199199

200200
if console in ["integratedTerminal", "externalTerminal"]:
201-
response = await self.send_request_async(
201+
await self.send_request_async(
202202
RunInTerminalRequest(
203203
arguments=RunInTerminalRequestArguments(
204204
cwd=cwd,
@@ -214,7 +214,6 @@ async def _launch(
214214
),
215215
return_type=RunInTerminalResponseBody,
216216
)
217-
print(response.process_id)
218217
elif console is None or console in ["internalConsole"]:
219218
run_env: Dict[str, Optional[str]] = dict(os.environ)
220219
run_env.update(env)

robotcode/debugger/protocol.py

Lines changed: 59 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import logging
77
import threading
8+
import traceback
89
from collections import OrderedDict
910
from typing import (
1011
Any,
@@ -98,7 +99,11 @@ def send_message(self, message: ProtocolMessage) -> None:
9899
)
99100

100101
if self.write_transport is not None:
101-
self.write_transport.write(header + body)
102+
msg = header + body
103+
if self._loop == asyncio.get_running_loop():
104+
self.write_transport.write(msg)
105+
elif self._loop is not None:
106+
self._loop.call_soon_threadsafe(self.write_transport.write, msg)
102107

103108
def send_error(
104109
self,
@@ -123,19 +128,10 @@ def _generate_json_rpc_messages_from_dict(
123128
data: Union[Dict[Any, Any], List[Dict[Any, Any]]]
124129
) -> Iterator[ProtocolMessage]:
125130
def inner(d: Dict[Any, Any]) -> ProtocolMessage:
126-
# if "type" in d:
127-
# type = d.get("type")
128-
# if type == "request":
129-
# return Request(**d)
130-
# elif type == "response":
131-
# if "success" in d and d["success"] is not True:
132-
# return ErrorResponse(**d)
133-
# return Response(**d)
134-
# elif type == "event":
135-
# return Event(**d)
136-
137-
# raise JsonRPCException(f"Invalid Debug Adapter Message {repr(d)}")
138-
return from_dict(d, (Request, Response, Event)) # type: ignore
131+
result = from_dict(d, (Request, Response, Event)) # type: ignore
132+
if isinstance(result, Response) and not result.success:
133+
result = from_dict(d, ErrorResponse)
134+
return result # type: ignore
139135

140136
if isinstance(data, list):
141137
for e in data:
@@ -150,7 +146,10 @@ def _handle_body(self, body: bytes, charset: str) -> None:
150146
raise
151147
except BaseException as e:
152148
self._logger.exception(e)
153-
self.send_error(f"Invalid Message: {type(e).__name__}: {str(e)} -> {str(body)}")
149+
self.send_error(
150+
f"Invalid Message: {type(e).__name__}: {str(e)} -> {str(body)}\n{traceback.format_exc()}",
151+
error_message=Message(traceback.format_exc()),
152+
)
154153

155154
def _handle_messages(self, iterator: Iterator[ProtocolMessage]) -> None:
156155
def done(f: asyncio.Future[Any]) -> None:
@@ -165,13 +164,13 @@ def done(f: asyncio.Future[Any]) -> None:
165164
@_logger.call
166165
async def handle_message(self, message: ProtocolMessage) -> None:
167166
if isinstance(message, Request):
168-
await self.handle_request(message)
167+
self.handle_request(message)
169168
if isinstance(message, Event):
170-
await self.handle_event(message)
169+
self.handle_event(message)
171170
elif isinstance(message, ErrorResponse):
172-
await self.handle_error_response(message)
171+
self.handle_error_response(message)
173172
elif isinstance(message, Response):
174-
await self.handle_response(message)
173+
self.handle_response(message)
175174

176175
@staticmethod
177176
def _convert_params(
@@ -237,56 +236,57 @@ async def handle_unknown_command(self, message: Request) -> Any:
237236
)
238237

239238
@_logger.call
240-
async def handle_request(self, message: Request) -> None:
239+
def handle_request(self, message: Request) -> None:
241240
e = self.registry.get_entry(message.command)
242241

243-
try:
242+
with self._received_request_lock:
244243
if e is None or not callable(e.method):
245244
result = asyncio.create_task(self.handle_unknown_command(message))
246245
else:
247246
params = self._convert_params(e.method, e.param_type, message.arguments)
248247

249248
result = asyncio.create_task(ensure_coroutine(e.method)(*params[0], **params[1]))
250249

251-
with self._received_request_lock:
252-
self._received_request[message.seq] = result
250+
self._received_request[message.seq] = result
253251

252+
def done(t: asyncio.Task[Any]) -> None:
254253
try:
255-
self.send_response(message.seq, message.command, await result)
254+
self.send_response(message.seq, message.command, t.result())
255+
except asyncio.CancelledError:
256+
self._logger.info(f"request message {repr(message)} canceled")
257+
except (SystemExit, KeyboardInterrupt):
258+
raise
259+
except DebugAdapterRPCErrorException as ex:
260+
self._logger.exception(ex)
261+
self.send_error(
262+
message=ex.message,
263+
request_seq=message.seq,
264+
command=ex.command or message.command,
265+
success=ex.success or False,
266+
error_message=ex.error_message,
267+
)
268+
except DebugAdapterErrorResponseError as ex:
269+
self.send_error(
270+
ex.error.message,
271+
message.seq,
272+
message.command,
273+
False,
274+
error_message=ex.error.body.error if ex.error.body is not None else None,
275+
)
276+
except BaseException as e:
277+
self._logger.exception(e)
278+
self.send_error(
279+
str(type(e).__name__),
280+
message.seq,
281+
message.command,
282+
False,
283+
error_message=Message(format=f"{type(e).__name__}: {e}", show_user=True),
284+
)
256285
finally:
257286
with self._received_request_lock:
258287
self._received_request.pop(message.seq, None)
259288

260-
except asyncio.CancelledError:
261-
self._logger.info(f"request message {repr(message)} canceled")
262-
except (SystemExit, KeyboardInterrupt):
263-
raise
264-
except DebugAdapterRPCErrorException as ex:
265-
self._logger.exception(ex)
266-
self.send_error(
267-
message=ex.message,
268-
request_seq=message.seq,
269-
command=ex.command or message.command,
270-
success=ex.success or False,
271-
error_message=ex.error_message,
272-
)
273-
except DebugAdapterErrorResponseError as ex:
274-
self.send_error(
275-
ex.error.message,
276-
message.seq,
277-
message.command,
278-
False,
279-
error_message=ex.error.body.error if ex.error.body is not None else None,
280-
)
281-
except BaseException as e:
282-
self._logger.exception(e)
283-
self.send_error(
284-
str(type(e).__name__),
285-
message.seq,
286-
message.command,
287-
False,
288-
error_message=Message(format=f"{type(e).__name__}: {e}", show_user=True),
289-
)
289+
result.add_done_callback(done)
290290

291291
@_logger.call
292292
def send_response(
@@ -332,26 +332,23 @@ async def send_event_async(self, event: Event) -> None:
332332
self.send_event(event)
333333

334334
@_logger.call
335-
async def handle_error_response(self, message: ErrorResponse) -> None:
335+
def handle_error_response(self, message: ErrorResponse) -> None:
336336
with self._sended_request_lock:
337337
entry = self._sended_request.pop(message.request_seq, None)
338338

339339
exception = DebugAdapterErrorResponseError(message)
340340
if entry is None:
341341
raise exception
342342

343-
try:
344-
entry.future.set_exception(exception)
345-
except (SystemExit, KeyboardInterrupt):
346-
raise
343+
entry.future.set_exception(exception)
347344

348345
@_logger.call
349-
async def handle_response(self, message: Response) -> None:
346+
def handle_response(self, message: Response) -> None:
350347
with self._sended_request_lock:
351348
entry = self._sended_request.pop(message.request_seq, None)
352349

353350
if entry is None:
354-
error = f"Invalid response. Could not find id '{message.request_seq}' in our request list"
351+
error = f"Invalid response. Could not find id '{message.request_seq}' in request list {message!r}"
355352
self._logger.warning(error)
356353
self.send_error("invalid response", error_message=Message(format=error, show_user=True))
357354
return
@@ -369,5 +366,5 @@ async def handle_response(self, message: Response) -> None:
369366
entry.future.set_exception(e)
370367

371368
@_logger.call
372-
async def handle_event(self, message: Event) -> None:
369+
def handle_event(self, message: Event) -> None:
373370
raise NotImplementedError()

robotcode/debugger/server.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import os
3+
from asyncio import Lock
34
from typing import Any, Literal, Optional, Union
45

56
from ..jsonrpc2.protocol import rpc_method
@@ -49,17 +50,16 @@ class LauncherServerProtocol(DebugAdapterProtocol):
4950

5051
def __init__(self) -> None:
5152
super().__init__()
52-
self._loop = asyncio.get_event_loop()
5353

5454
self._initialized = False
5555
self._connected_event = asyncio.Event()
5656
self._connected = False
5757
self._sigint_signaled = False
5858

59-
self._exited_lock = asyncio.Lock()
59+
self._exited_lock = Lock()
6060
self._exited = False
6161

62-
self._terminated_lock = asyncio.Lock()
62+
self._terminated_lock = Lock()
6363
self._terminated = False
6464

6565
self._received_configuration_done_event = asyncio.Event()
@@ -68,7 +68,8 @@ def __init__(self) -> None:
6868
Debugger.instance().send_event.add(self.on_debugger_send_event)
6969

7070
def on_debugger_send_event(self, sender: Any, event: Event) -> None:
71-
self._loop.call_soon_threadsafe(self.send_event, event)
71+
if self._loop is not None:
72+
self._loop.call_soon_threadsafe(self.send_event, event)
7273

7374
@property
7475
def connected(self) -> bool:

0 commit comments

Comments
 (0)