Skip to content

Commit 4b3e65c

Browse files
committed
refactor: move some diagnostics code from langserver package to robot package
1 parent 3798c5e commit 4b3e65c

File tree

24 files changed

+192
-182
lines changed

24 files changed

+192
-182
lines changed

packages/core/src/robotcode/core/event.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
Optional,
1414
Type,
1515
TypeVar,
16+
Union,
1617
cast,
1718
)
1819

@@ -74,12 +75,15 @@ def _notify(
7475
*__args: _TParams.args,
7576
callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None,
7677
**__kwargs: _TParams.kwargs,
77-
) -> Iterator[_TResult]:
78+
) -> Iterator[Union[_TResult, BaseException]]:
7879
for method in filter(
7980
lambda x: callback_filter(x) if callback_filter is not None else True,
8081
set(self),
8182
):
82-
yield method(*__args, **__kwargs)
83+
try:
84+
yield method(*__args, **__kwargs)
85+
except BaseException as e:
86+
yield e
8387

8488

8589
class EventIterator(EventResultIteratorBase[_TParams, _TResult]):
@@ -88,7 +92,7 @@ def __call__(
8892
*__args: _TParams.args,
8993
callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None,
9094
**__kwargs: _TParams.kwargs,
91-
) -> Iterator[_TResult]:
95+
) -> Iterator[Union[_TResult, BaseException]]:
9296
return self._notify(*__args, callback_filter=callback_filter, **__kwargs)
9397

9498

@@ -98,7 +102,7 @@ def __call__(
98102
*__args: _TParams.args,
99103
callback_filter: Optional[Callable[[Callable[..., Any]], bool]] = None,
100104
**__kwargs: _TParams.kwargs,
101-
) -> List[_TResult]:
105+
) -> List[Union[_TResult, BaseException]]:
102106
return list(self._notify(*__args, callback_filter=callback_filter, **__kwargs))
103107

104108

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
import inspect
12
from typing import Any, Callable, TypeVar
23

34
_F = TypeVar("_F", bound=Callable[..., Any])
45

6+
__THREADED_MARKER = "__threaded__"
7+
58

69
def threaded(enabled: bool = True) -> Callable[[_F], _F]:
710
def decorator(func: _F) -> _F:
8-
setattr(func, "__threaded__", enabled)
11+
setattr(func, __THREADED_MARKER, enabled)
912
return func
1013

1114
return decorator
1215

1316

1417
def is_threaded_callable(func: Callable[..., Any]) -> bool:
15-
return getattr(func, "__threaded__", False)
18+
return getattr(func, __THREADED_MARKER, False) or inspect.ismethod(func) and getattr(func, __THREADED_MARKER, False)

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

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json
77
import re
88
import threading
9+
import time
910
import weakref
1011
from abc import ABC, abstractmethod
1112
from collections import OrderedDict
@@ -443,6 +444,7 @@ def __init__(self) -> None:
443444
self._sended_request_count = 0
444445
self._received_request: OrderedDict[Union[str, int, None], ReceivedRequestEntry] = OrderedDict()
445446
self._received_request_lock = threading.RLock()
447+
self._signature_cache: Dict[Callable[..., Any], inspect.Signature] = {}
446448

447449
@staticmethod
448450
def _generate_json_rpc_messages_from_dict(
@@ -640,9 +642,8 @@ async def handle_error(self, message: JsonRPCError) -> None:
640642
if not entry.future.done():
641643
entry.future.set_exception(e)
642644

643-
@staticmethod
644645
def _convert_params(
645-
callable: Callable[..., Any], params_type: Optional[Type[Any]], params: Any
646+
self, callable: Callable[..., Any], params_type: Optional[Type[Any]], params: Any
646647
) -> Tuple[List[Any], Dict[str, Any]]:
647648
if params is None:
648649
return [], {}
@@ -655,7 +656,12 @@ def _convert_params(
655656
# try to convert the dict to correct type
656657
converted_params = from_dict(params, params_type)
657658

658-
signature = inspect.signature(callable)
659+
# get the signature of the callable
660+
if callable in self._signature_cache:
661+
signature = self._signature_cache[callable]
662+
else:
663+
signature = inspect.signature(callable)
664+
self._signature_cache[callable] = signature
659665

660666
has_var_kw = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in signature.parameters.values())
661667

@@ -705,62 +711,66 @@ def _convert_params(
705711
return args, kw_args
706712

707713
async def handle_request(self, message: JsonRPCRequest) -> None:
708-
e = self.registry.get_entry(message.method)
714+
start = time.monotonic_ns()
715+
try:
716+
e = self.registry.get_entry(message.method)
709717

710-
if e is None or not callable(e.method):
711-
self.send_error(
712-
JsonRPCErrors.METHOD_NOT_FOUND,
713-
f"Unknown method: {message.method}",
714-
id=message.id,
715-
)
716-
return
718+
if e is None or not callable(e.method):
719+
self.send_error(
720+
JsonRPCErrors.METHOD_NOT_FOUND,
721+
f"Unknown method: {message.method}",
722+
id=message.id,
723+
)
724+
return
717725

718-
params = self._convert_params(e.method, e.param_type, message.params)
726+
params = self._convert_params(e.method, e.param_type, message.params)
719727

720-
if not e.is_coroutine:
721-
self.send_response(message.id, e.method(*params[0], **params[1]))
722-
else:
723-
if is_threaded_callable(e.method) or inspect.ismethod(e.method) and is_threaded_callable(e.method.__func__):
724-
task = run_coroutine_in_thread(
725-
ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1]
726-
)
728+
if not e.is_coroutine:
729+
self.send_response(message.id, e.method(*params[0], **params[1]))
727730
else:
728-
task = create_sub_task(
729-
ensure_coroutine(e.method)(*params[0], **params[1]),
730-
name=message.method,
731-
)
731+
if is_threaded_callable(e.method):
732+
task = run_coroutine_in_thread(
733+
ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1]
734+
)
735+
else:
736+
task = create_sub_task(
737+
ensure_coroutine(e.method)(*params[0], **params[1]),
738+
name=message.method,
739+
)
740+
741+
with self._received_request_lock:
742+
self._received_request[message.id] = ReceivedRequestEntry(task, message, e.cancelable)
743+
744+
def done(t: asyncio.Future[Any]) -> None:
745+
try:
746+
if not t.cancelled():
747+
ex = t.exception()
748+
if ex is not None:
749+
self.__logger.exception(ex, exc_info=ex)
750+
raise JsonRPCErrorException(
751+
JsonRPCErrors.INTERNAL_ERROR, f"{type(ex).__name__}: {ex}"
752+
) from ex
753+
754+
self.send_response(message.id, t.result())
755+
except asyncio.CancelledError:
756+
self.__logger.debug(lambda: f"request message {message!r} canceled")
757+
self.send_error(JsonRPCErrors.REQUEST_CANCELLED, "Request canceled.", id=message.id)
758+
except (SystemExit, KeyboardInterrupt):
759+
raise
760+
except JsonRPCErrorException as e:
761+
self.send_error(e.code, e.message or f"{type(e).__name__}: {e}", id=message.id, data=e.data)
762+
except BaseException as e:
763+
self.__logger.exception(e)
764+
self.send_error(JsonRPCErrors.INTERNAL_ERROR, f"{type(e).__name__}: {e}", id=message.id)
765+
finally:
766+
with self._received_request_lock:
767+
self._received_request.pop(message.id, None)
768+
769+
task.add_done_callback(done)
732770

733-
with self._received_request_lock:
734-
self._received_request[message.id] = ReceivedRequestEntry(task, message, e.cancelable)
735-
736-
def done(t: asyncio.Future[Any]) -> None:
737-
try:
738-
if not t.cancelled():
739-
ex = t.exception()
740-
if ex is not None:
741-
self.__logger.exception(ex, exc_info=ex)
742-
raise JsonRPCErrorException(
743-
JsonRPCErrors.INTERNAL_ERROR, f"{type(ex).__name__}: {ex}"
744-
) from ex
745-
746-
self.send_response(message.id, t.result())
747-
except asyncio.CancelledError:
748-
self.__logger.debug(lambda: f"request message {message!r} canceled")
749-
self.send_error(JsonRPCErrors.REQUEST_CANCELLED, "Request canceled.", id=message.id)
750-
except (SystemExit, KeyboardInterrupt):
751-
raise
752-
except JsonRPCErrorException as e:
753-
self.send_error(e.code, e.message or f"{type(e).__name__}: {e}", id=message.id, data=e.data)
754-
except BaseException as e:
755-
self.__logger.exception(e)
756-
self.send_error(JsonRPCErrors.INTERNAL_ERROR, f"{type(e).__name__}: {e}", id=message.id)
757-
finally:
758-
with self._received_request_lock:
759-
self._received_request.pop(message.id, None)
760-
761-
task.add_done_callback(done)
762-
763-
await task
771+
await task
772+
finally:
773+
self.__logger.debug(lambda: f"request message {message!r} done in {time.monotonic_ns() - start}ns")
764774

765775
def cancel_request(self, id: Union[int, str, None]) -> None:
766776
with self._received_request_lock:
@@ -788,11 +798,7 @@ async def handle_notification(self, message: JsonRPCNotification) -> None:
788798
if not e.is_coroutine:
789799
e.method(*params[0], **params[1])
790800
else:
791-
if (
792-
is_threaded_callable(e.method)
793-
or inspect.ismethod(e.method)
794-
and is_threaded_callable(e.method.__func__)
795-
):
801+
if is_threaded_callable(e.method):
796802
task = run_coroutine_in_thread(
797803
ensure_coroutine(cast(Callable[..., Any], e.method)), *params[0], **params[1]
798804
)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ def read_document_text(self, uri: Uri, language_id: Union[str, Callable[[Any], b
114114
for e in self.on_read_document_text(
115115
self, uri, callback_filter=language_id_filter(language_id) if isinstance(language_id, str) else language_id
116116
):
117+
if isinstance(e, BaseException):
118+
raise RuntimeError(f"Can't read document text from {uri}: {e}") from e
119+
117120
if e is not None:
118121
return self._normalize_line_endings(e)
119122

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
from __future__ import annotations
2-
3-
from asyncio import CancelledError
41
from typing import TYPE_CHECKING, Any, Final, List, Optional
52

6-
from robotcode.core.async_tools import async_tasking_event
3+
from robotcode.core.event import event
74
from robotcode.core.lsp.types import (
85
Hover,
96
HoverOptions,
@@ -31,11 +28,11 @@
3128
class HoverProtocolPart(LanguageServerProtocolPart, HasExtendCapabilities):
3229
_logger: Final = LoggingDescriptor()
3330

34-
def __init__(self, parent: LanguageServerProtocol) -> None:
31+
def __init__(self, parent: "LanguageServerProtocol") -> None:
3532
super().__init__(parent)
3633

37-
@async_tasking_event
38-
async def collect(sender, document: TextDocument, position: Position) -> Optional[Hover]: # NOSONAR
34+
@event
35+
def collect(sender, document: TextDocument, position: Position) -> Optional[Hover]:
3936
...
4037

4138
def extend_capabilities(self, capabilities: ServerCapabilities) -> None:
@@ -44,7 +41,7 @@ def extend_capabilities(self, capabilities: ServerCapabilities) -> None:
4441

4542
@rpc_method(name="textDocument/hover", param_type=HoverParams)
4643
@threaded()
47-
async def _text_document_hover(
44+
def _text_document_hover(
4845
self,
4946
text_document: TextDocumentIdentifier,
5047
position: Position,
@@ -57,15 +54,14 @@ async def _text_document_hover(
5754
if document is None:
5855
return None
5956

60-
for result in await self.collect(
57+
for result in self.collect(
6158
self,
6259
document,
6360
document.position_from_utf16(position),
6461
callback_filter=language_id_filter(document),
6562
):
6663
if isinstance(result, BaseException):
67-
if not isinstance(result, CancelledError):
68-
self._logger.exception(result, exc_info=result)
64+
self._logger.exception(result, exc_info=result)
6965
else:
7066
if result is not None:
7167
results.append(result)

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/analyzer.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@
3737
Range,
3838
)
3939
from robotcode.core.uri import Uri
40+
from robotcode.robot.diagnostics.entities import (
41+
ArgumentDefinition,
42+
CommandLineVariableDefinition,
43+
EnvironmentVariableDefinition,
44+
LibraryEntry,
45+
LocalVariableDefinition,
46+
VariableDefinition,
47+
VariableDefinitionType,
48+
VariableNotFoundDefinition,
49+
)
50+
from robotcode.robot.diagnostics.library_doc import KeywordDoc, is_embedded_keyword
4051
from robotcode.robot.utils import get_robot_version
4152
from robotcode.robot.utils.ast import (
4253
is_not_variable_token,
@@ -48,18 +59,7 @@
4859
)
4960
from robotcode.robot.utils.visitor import Visitor
5061

51-
from .entities import (
52-
ArgumentDefinition,
53-
CommandLineVariableDefinition,
54-
EnvironmentVariableDefinition,
55-
LibraryEntry,
56-
LocalVariableDefinition,
57-
VariableDefinition,
58-
VariableDefinitionType,
59-
VariableNotFoundDefinition,
60-
)
6162
from .errors import DIAGNOSTICS_SOURCE_NAME, Error
62-
from .library_doc import KeywordDoc, is_embedded_keyword
6363
from .model_helper import ModelHelperMixin
6464
from .namespace import (
6565
KeywordFinder,

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/imports_manager.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,8 @@
3838
from robotcode.language_server.common.parts.workspace import FileWatcherEntry, Workspace
3939
from robotcode.language_server.common.text_document import TextDocument
4040
from robotcode.language_server.robotframework.configuration import CacheSaveLocation, RobotCodeConfig
41-
from robotcode.robot.utils import get_robot_version, get_robot_version_str
42-
from robotcode.robot.utils.robot_path import find_file_ex
43-
44-
from ...__version__ import __version__
45-
from .entities import CommandLineVariableDefinition, VariableDefinition
46-
from .library_doc import (
41+
from robotcode.robot.diagnostics.entities import CommandLineVariableDefinition, VariableDefinition
42+
from robotcode.robot.diagnostics.library_doc import (
4743
ROBOT_LIBRARY_PACKAGE,
4844
CompleteResult,
4945
LibraryDoc,
@@ -64,6 +60,10 @@
6460
resolve_args,
6561
resolve_variable,
6662
)
63+
from robotcode.robot.utils import get_robot_version, get_robot_version_str
64+
from robotcode.robot.utils.robot_path import find_file_ex
65+
66+
from ...__version__ import __version__
6767

6868
if TYPE_CHECKING:
6969
from robotcode.language_server.robotframework.protocol import RobotLanguageServerProtocol

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/model_helper.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@
2222
from robot.variables.finders import NOT_FOUND, NumberFinder
2323
from robot.variables.search import contains_variable, search_variable
2424
from robotcode.core.lsp.types import Position
25+
from robotcode.robot.diagnostics.entities import (
26+
LibraryEntry,
27+
VariableDefinition,
28+
VariableNotFoundDefinition,
29+
)
30+
from robotcode.robot.diagnostics.library_doc import (
31+
ArgumentInfo,
32+
KeywordArgumentKind,
33+
KeywordDoc,
34+
LibraryDoc,
35+
)
2536
from robotcode.robot.utils import get_robot_version
2637
from robotcode.robot.utils.ast import (
2738
iter_over_keyword_names_and_owners,
@@ -32,17 +43,6 @@
3243
whitespace_from_begin_of_token,
3344
)
3445

35-
from .entities import (
36-
LibraryEntry,
37-
VariableDefinition,
38-
VariableNotFoundDefinition,
39-
)
40-
from .library_doc import (
41-
ArgumentInfo,
42-
KeywordArgumentKind,
43-
KeywordDoc,
44-
LibraryDoc,
45-
)
4646
from .namespace import (
4747
DEFAULT_BDD_PREFIXES,
4848
Namespace,

0 commit comments

Comments
 (0)