Skip to content

Commit

Permalink
feat(debugger): display a virtual variable in the local scope with de…
Browse files Browse the repository at this point in the history
…tails about the current exception when an exception breakpoint is triggered
  • Loading branch information
d-biehl committed Jan 14, 2025
1 parent b155bd1 commit 4690071
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 93 deletions.
33 changes: 28 additions & 5 deletions packages/debugger/src/robotcode/debugger/debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
Sequence,
Set,
Tuple,
TypedDict,
Union,
cast,
)
Expand Down Expand Up @@ -283,6 +284,12 @@ def end_keyword(self, data: running.Keyword, result: result.Keyword) -> None:
breakpoint_id_manager = IdManager()


class ExceptionInformation(TypedDict):
text: Optional[str]
description: str
status: str


class Debugger:
__instance: ClassVar[Optional["Debugger"]] = None
__lock: ClassVar = threading.RLock()
Expand Down Expand Up @@ -356,6 +363,7 @@ def __init__(self) -> None:
self._evaluate_cache: List[Any] = []
self._variables_cache: Dict[int, Any] = {}
self._variables_object_cache: List[Any] = []
self._current_exception: Optional[ExceptionInformation] = None

@property
def state(self) -> State:
Expand Down Expand Up @@ -726,6 +734,8 @@ def process_end_state(
self.requested_state = RequestedState.Nothing
self.state = State.Paused

self._current_exception = {"text": text, "description": description, "status": status}

self.send_event(
self,
StoppedEvent(
Expand Down Expand Up @@ -779,6 +789,7 @@ def wait_for_running(self) -> None:
continue

break
self._current_exception = None

def start_output_group(self, name: str, attributes: Dict[str, Any], type: Optional[str] = None) -> None:
if self.group_output:
Expand Down Expand Up @@ -993,7 +1004,7 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None:
if status == "FAIL":
self.process_end_state(
status,
{"failed_test"},
{""},
"Test failed.",
f"Test failed{f': {v}' if (v := attributes.get('message')) else ''}",
)
Expand Down Expand Up @@ -1158,7 +1169,7 @@ def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
{"uncaught_failed_keyword"}
if self.is_not_caughted_by_keyword()
and self.is_not_caugthed_by_except(self.last_fail_message)
else {}
else []
),
},
"Keyword failed.",
Expand Down Expand Up @@ -1413,7 +1424,9 @@ def _new_cache_id(self) -> int:

debug_repr = DebugRepr()

def _create_variable(self, name: str, value: Any) -> Variable:
def _create_variable(
self, name: str, value: Any, presentation_hint: Optional[VariablePresentationHint] = None
) -> Variable:
if isinstance(value, Mapping):
v_id = self._new_cache_id()
self._variables_cache[v_id] = value
Expand All @@ -1424,7 +1437,9 @@ def _create_variable(self, name: str, value: Any) -> Variable:
variables_reference=v_id,
named_variables=len(value) + 1,
indexed_variables=0,
presentation_hint=VariablePresentationHint(kind="data"),
presentation_hint=(
presentation_hint if presentation_hint is not None else VariablePresentationHint(kind="data")
),
)

if isinstance(value, Sequence) and not isinstance(value, str):
Expand Down Expand Up @@ -1502,6 +1517,14 @@ def get_variables(
)
elif entry.local_id == variables_reference:
vars = entry.get_first_or_self().variables()

if self._current_exception is not None:
result["${EXCEPTION}"] = self._create_variable(
"${EXCEPTION}",
self._current_exception,
VariablePresentationHint(kind="virtual"),
)

if vars is not None:
p = entry.parent() if entry.parent else None

Expand Down Expand Up @@ -1880,7 +1903,7 @@ def set_exception_breakpoints(
if option.filter_id in [
"failed_keyword",
"uncaught_failed_keyword",
"failed_test",
"",
"failed_suite",
]:
entry = ExceptionBreakpointsEntry(
Expand Down
48 changes: 48 additions & 0 deletions packages/debugger/src/robotcode/debugger/default_capabilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from .dap_types import Capabilities, ExceptionBreakpointsFilter

DFEAULT_CAPABILITIES = Capabilities(
supports_configuration_done_request=True,
supports_conditional_breakpoints=True,
supports_hit_conditional_breakpoints=True,
support_terminate_debuggee=True,
supports_evaluate_for_hovers=True,
supports_terminate_request=True,
supports_log_points=True,
supports_set_expression=True,
supports_set_variable=True,
supports_value_formatting_options=True,
exception_breakpoint_filters=[
ExceptionBreakpointsFilter(
filter="failed_keyword",
label="Failed Keywords",
description="Breaks on failed keywords",
default=False,
supports_condition=True,
),
ExceptionBreakpointsFilter(
filter="uncaught_failed_keyword",
label="Uncaught Failed Keywords",
description="Breaks on uncaught failed keywords",
default=True,
supports_condition=True,
),
ExceptionBreakpointsFilter(
filter="failed_test",
label="Failed Test",
description="Breaks on failed tests",
default=False,
supports_condition=True,
),
ExceptionBreakpointsFilter(
filter="failed_suite",
label="Failed Suite",
description="Breaks on failed suite",
default=False,
supports_condition=True,
),
],
supports_exception_options=True,
supports_exception_filter_options=True,
supports_completions_request=True,
supports_a_n_s_i_styling=True,
)
46 changes: 2 additions & 44 deletions packages/debugger/src/robotcode/debugger/launcher/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
DisconnectArguments,
DisconnectRequest,
Event,
ExceptionBreakpointsFilter,
InitializeRequest,
InitializeRequestArguments,
LaunchRequestArguments,
Expand All @@ -39,6 +38,7 @@
TerminatedEvent,
TerminateRequest,
)
from ..default_capabilities import DFEAULT_CAPABILITIES
from ..protocol import DebugAdapterProtocol
from .client import DAPClient, DAPClientError

Expand Down Expand Up @@ -91,49 +91,7 @@ async def _initialize(self, arguments: InitializeRequestArguments, *args: Any, *

self._initialized = True

return Capabilities(
supports_configuration_done_request=True,
supports_conditional_breakpoints=True,
supports_hit_conditional_breakpoints=True,
support_terminate_debuggee=True,
# support_suspend_debuggee=True,
supports_evaluate_for_hovers=True,
supports_terminate_request=True,
supports_log_points=True,
supports_set_expression=True,
supports_set_variable=True,
supports_value_formatting_options=True,
exception_breakpoint_filters=[
ExceptionBreakpointsFilter(
filter="failed_keyword",
label="Failed Keywords",
description="Breaks on failed keywords",
default=False,
),
ExceptionBreakpointsFilter(
filter="uncaught_failed_keyword",
label="Uncaught Failed Keywords",
description="Breaks on uncaught failed keywords",
default=True,
),
ExceptionBreakpointsFilter(
filter="failed_test",
label="Failed Test",
description="Breaks on failed tests",
default=False,
),
ExceptionBreakpointsFilter(
filter="failed_suite",
label="Failed Suite",
description="Breaks on failed suite",
default=False,
),
],
supports_exception_options=True,
supports_exception_filter_options=True,
supports_completions_request=True,
supports_a_n_s_i_styling=True,
)
return DFEAULT_CAPABILITIES

@rpc_method(name="launch", param_type=LaunchRequestArguments)
async def _launch(
Expand Down
46 changes: 2 additions & 44 deletions packages/debugger/src/robotcode/debugger/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
EvaluateArguments,
EvaluateResponseBody,
Event,
ExceptionBreakpointsFilter,
ExitedEvent,
ExitedEventBody,
InitializedEvent,
Expand All @@ -50,6 +49,7 @@
VariablesResponseBody,
)
from .debugger import Debugger, PathMapping
from .default_capabilities import DFEAULT_CAPABILITIES
from .protocol import DebugAdapterProtocol

TCP_DEFAULT_PORT = 6612
Expand Down Expand Up @@ -144,49 +144,7 @@ async def _initialize(self, arguments: InitializeRequestArguments, *args: Any, *
if self.loop is not None:
self.loop.call_soon(self.initialized)

return Capabilities(
supports_configuration_done_request=True,
supports_conditional_breakpoints=True,
supports_hit_conditional_breakpoints=True,
support_terminate_debuggee=True,
# support_suspend_debuggee=True,
supports_evaluate_for_hovers=True,
supports_terminate_request=True,
supports_log_points=True,
supports_set_expression=True,
supports_set_variable=True,
supports_value_formatting_options=True,
exception_breakpoint_filters=[
ExceptionBreakpointsFilter(
filter="failed_keyword",
label="Failed Keywords",
description="Breaks on failed keywords",
default=False,
),
ExceptionBreakpointsFilter(
filter="uncaught_failed_keyword",
label="Uncaught Failed Keywords",
description="Breaks on uncaught failed keywords",
default=True,
),
ExceptionBreakpointsFilter(
filter="failed_test",
label="Failed Test",
description="Breaks on failed tests",
default=False,
),
ExceptionBreakpointsFilter(
filter="failed_suite",
label="Failed Suite",
description="Breaks on failed suite",
default=False,
),
],
supports_exception_options=True,
supports_exception_filter_options=True,
supports_completions_request=True,
supports_a_n_s_i_styling=True,
)
return DFEAULT_CAPABILITIES

@rpc_method(name="attach", param_type=AttachRequestArguments)
async def _attach(
Expand Down

0 comments on commit 4690071

Please sign in to comment.