Skip to content

Commit 7f688cb

Browse files
committed
implement "Uncaughted Filed Keywords" exception breakpoint
1 parent 31bf921 commit 7f688cb

File tree

2 files changed

+59
-28
lines changed

2 files changed

+59
-28
lines changed

robotcode/debugger/debugger.py

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,11 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Any:
212212

213213
def __init__(self) -> None:
214214
self.breakpoints: Dict[str, BreakpointsEntry] = {}
215+
215216
self.exception_breakpoints: Set[ExceptionBreakpointsEntry] = set()
217+
self.exception_breakpoints.add(
218+
ExceptionBreakpointsEntry((), (ExceptionFilterOptions("uncaughted_failed_keyword"),), ())
219+
)
216220

217221
self.main_thread: Optional[threading.Thread] = None
218222
self.full_stack_frames: Deque[StackFrameEntry] = deque()
@@ -528,14 +532,14 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
528532
),
529533
)
530534

531-
def process_end_state(self, status: str, filter_id: str, description: str, text: Optional[str]) -> None:
535+
def process_end_state(self, status: str, filter_id: Set[str], description: str, text: Optional[str]) -> None:
532536
if (
533537
not self.terminated
534538
and status == "FAIL"
535539
and any(
536540
v
537541
for v in self.exception_breakpoints
538-
if v.filter_options is not None and any(o for o in v.filter_options if o.filter_id == filter_id)
542+
if v.filter_options and any(o for o in v.filter_options if o.filter_id in filter_id)
539543
)
540544
):
541545
self.state = State.Paused
@@ -633,6 +637,11 @@ def add_stackframe_entry(
633637
longname=longname,
634638
)
635639

640+
self.full_stack_frames.appendleft(result)
641+
642+
if type in ["KEYWORD"] and source is None and line is None and column is None:
643+
return result
644+
636645
if type in ["SUITE", "TEST"]:
637646
self.stack_frames.appendleft(result)
638647
elif type in ["KEYWORD", "SETUP", "TEARDOWN"] and isinstance(handler, UserKeywordHandler):
@@ -644,8 +653,6 @@ def add_stackframe_entry(
644653
if self.stack_frames:
645654
self.stack_frames[0].stack_frames.appendleft(result)
646655

647-
self.full_stack_frames.appendleft(result)
648-
649656
return result
650657

651658
def remove_stackframe_entry(
@@ -654,12 +661,17 @@ def remove_stackframe_entry(
654661
type: str,
655662
source: Optional[str],
656663
line: Optional[int],
657-
column: Optional[int] = 1,
664+
column: Optional[int] = None,
658665
*,
659666
handler: Any = None,
660667
) -> None:
661668
from robot.running.userkeyword import UserKeywordHandler
662669

670+
self.full_stack_frames.popleft()
671+
672+
if type in ["KEYWORD"] and source is None and line is None and column is None:
673+
return
674+
663675
if type in ["SUITE", "TEST"]:
664676
self.stack_frames.popleft()
665677
elif type in ["KEYWORD", "SETUP", "TEARDOWN"] and isinstance(handler, UserKeywordHandler):
@@ -671,8 +683,6 @@ def remove_stackframe_entry(
671683
if self.stack_frames:
672684
self.stack_frames[0].stack_frames.popleft()
673685

674-
self.full_stack_frames.popleft()
675-
676686
def start_suite(self, name: str, attributes: Dict[str, Any]) -> None:
677687
source = attributes.get("source", None)
678688
line_no = attributes.get("lineno", 1)
@@ -707,12 +717,13 @@ def end_suite(self, name: str, attributes: Dict[str, Any]) -> None:
707717
if self.debug:
708718
status = attributes.get("status", "")
709719

710-
self.process_end_state(
711-
status,
712-
"failed_suite",
713-
"Suite failed.",
714-
f"Suite failed{f': {v}' if (v:=attributes.get('message', None)) else ''}",
715-
)
720+
if status == "FAIL":
721+
self.process_end_state(
722+
status,
723+
{"failed_suite"},
724+
"Suite failed.",
725+
f"Suite failed{f': {v}' if (v:=attributes.get('message', None)) else ''}",
726+
)
716727

717728
source = attributes.get("source", None)
718729
line_no = attributes.get("lineno", 1)
@@ -739,12 +750,13 @@ def end_test(self, name: str, attributes: Dict[str, Any]) -> None:
739750
if self.debug:
740751
status = attributes.get("status", "")
741752

742-
self.process_end_state(
743-
status,
744-
"failed_test",
745-
"Test failed.",
746-
f"Test failed{f': {v}' if (v:=attributes.get('message', None)) else ''}",
747-
)
753+
if status == "FAIL":
754+
self.process_end_state(
755+
status,
756+
{"failed_test"},
757+
"Test failed.",
758+
f"Test failed{f': {v}' if (v:=attributes.get('message', None)) else ''}",
759+
)
748760

749761
source = attributes.get("source", None)
750762
line_no = attributes.get("lineno", 1)
@@ -784,17 +796,36 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
784796

785797
self.wait_for_running()
786798

799+
CAUGHTED_KEYWORDS = [
800+
"BuiltIn.Run Keyword And Expect Error",
801+
"BuiltIn.Run Keyword And Ignore Error",
802+
"BuiltIn.Run Keyword And Warn On Failure",
803+
"BuiltIn.Wait Until Keyword Succeeds",
804+
"BuiltIn.Run Keyword And Continue On Failure",
805+
]
806+
807+
def in_caughted_keyword(self) -> bool:
808+
r = next(
809+
(
810+
v
811+
for v in itertools.islice(self.full_stack_frames, 1, None)
812+
if v.type == "KEYWORD" and v.longname in self.CAUGHTED_KEYWORDS
813+
),
814+
None,
815+
)
816+
return r is None
817+
787818
def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
788819
from robot.running.context import EXECUTION_CONTEXTS
789820

790821
type = attributes.get("type", None)
791822
if self.debug:
792823
status = attributes.get("status", "")
793824

794-
if status != "NOT RUN" and type in ["KEYWORD", "SETUP", "TEARDOWN"]:
825+
if status == "FAIL" and type in ["KEYWORD", "SETUP", "TEARDOWN"]:
795826
self.process_end_state(
796827
status,
797-
"failed_keyword",
828+
{"failed_keyword", *({"uncaughted_failed_keyword"} if self.in_caughted_keyword() else {})},
798829
"Keyword failed.",
799830
f"Keyword failed: {self.last_fail_message}" if self.last_fail_message else "Keyword failed.",
800831
)
@@ -1149,7 +1180,7 @@ def set_exception_breakpoints(
11491180

11501181
if filter_options is not None:
11511182
for option in filter_options:
1152-
if option.filter_id in ["failed_keyword", "failed_test", "failed_suite"]:
1183+
if option.filter_id in ["failed_keyword", "uncaughted_failed_keyword", "failed_test", "failed_suite"]:
11531184
entry = ExceptionBreakpointsEntry(
11541185
tuple(filters),
11551186
tuple(filter_options) if filter_options is not None else None,

robotcode/debugger/launcher/server.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,25 +94,25 @@ async def _initialize(self, arguments: InitializeRequestArguments, *args: Any, *
9494
filter="failed_keyword",
9595
label="Failed Keywords",
9696
description="Breaks on failed keywords",
97+
default=False,
98+
),
99+
ExceptionBreakpointsFilter(
100+
filter="uncaughted_failed_keyword",
101+
label="Uncaughted Failed Keywords",
102+
description="Breaks on uncaughted failed keywords",
97103
default=True,
98-
# supports_condition=True,
99-
# condition_description="expression",
100104
),
101105
ExceptionBreakpointsFilter(
102106
filter="failed_test",
103107
label="Failed Test",
104108
description="Breaks on failed tests",
105109
default=False,
106-
# supports_condition=True,
107-
# condition_description="expression",
108110
),
109111
ExceptionBreakpointsFilter(
110112
filter="failed_suite",
111113
label="Failed Suite",
112114
description="Breaks on failed suite",
113115
default=False,
114-
# supports_condition=True,
115-
# condition_description="expression",
116116
),
117117
],
118118
supports_exception_options=True,

0 commit comments

Comments
 (0)