6
6
import pathlib
7
7
import re
8
8
import threading
9
+ import time
9
10
import weakref
10
11
from collections import deque
11
12
from enum import Enum
@@ -254,7 +255,7 @@ def __init__(self) -> None:
254
255
self .full_stack_frames : Deque [StackFrameEntry ] = deque ()
255
256
self .stack_frames : Deque [StackFrameEntry ] = deque ()
256
257
self .condition = threading .Condition ()
257
- self .state : State = State .Stopped
258
+ self ._state : State = State .Stopped
258
259
self .requested_state : RequestedState = RequestedState .Nothing
259
260
self .stop_stack_len = 0
260
261
self ._robot_report_file : Optional [str ] = None
@@ -282,6 +283,18 @@ def __init__(self) -> None:
282
283
self ._after_evaluate_keyword_event .set ()
283
284
self .expression_mode = False
284
285
286
+ @property
287
+ def state (self ) -> State :
288
+ return self ._state
289
+
290
+ @state .setter
291
+ def state (self , value : State ) -> None :
292
+ if self ._state == value :
293
+ # if state is not changed, do nothing and wait a little bit to avoid busy loop
294
+ time .sleep (0.01 )
295
+
296
+ self ._state = value
297
+
285
298
@property
286
299
def debug (self ) -> bool :
287
300
return self ._debug
@@ -476,8 +489,8 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
476
489
477
490
elif self .requested_state == RequestedState .Next :
478
491
if len (self .full_stack_frames ) <= self .stop_stack_len :
479
- self .state = State .Paused
480
492
self .requested_state = RequestedState .Nothing
493
+ self .state = State .Paused
481
494
482
495
self .send_event (
483
496
self ,
@@ -490,8 +503,8 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
490
503
)
491
504
492
505
elif self .requested_state == RequestedState .StepIn :
493
- self .state = State .Paused
494
506
self .requested_state = RequestedState .Nothing
507
+ self .state = State .Paused
495
508
496
509
self .send_event (
497
510
self ,
@@ -504,8 +517,8 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
504
517
)
505
518
506
519
elif self .requested_state == RequestedState .StepOut and len (self .full_stack_frames ) <= self .stop_stack_len :
507
- self .state = State .Paused
508
520
self .requested_state = RequestedState .Nothing
521
+ self .state = State .Paused
509
522
510
523
self .send_event (
511
524
self ,
@@ -570,8 +583,8 @@ def process_start_state(self, source: str, line_no: int, type: str, status: str)
570
583
)
571
584
return
572
585
573
- self .state = State .Paused
574
586
self .requested_state = RequestedState .Nothing
587
+ self .state = State .Paused
575
588
576
589
self .send_event (
577
590
self ,
@@ -588,49 +601,6 @@ def process_end_state(self, status: str, filter_id: Set[str], description: str,
588
601
if self .state == State .Stopped :
589
602
return
590
603
591
- if self .requested_state == RequestedState .Next :
592
- if len (self .full_stack_frames ) <= self .stop_stack_len :
593
- self .state = State .Paused
594
- self .requested_state = RequestedState .Nothing
595
-
596
- self .send_event (
597
- self ,
598
- StoppedEvent (
599
- body = StoppedEventBody (
600
- reason = StoppedReason .STEP ,
601
- thread_id = threading .current_thread ().ident ,
602
- )
603
- ),
604
- )
605
-
606
- elif self .requested_state == RequestedState .StepIn :
607
- self .state = State .Paused
608
- self .requested_state = RequestedState .Nothing
609
-
610
- self .send_event (
611
- self ,
612
- StoppedEvent (
613
- body = StoppedEventBody (
614
- reason = StoppedReason .STEP ,
615
- thread_id = threading .current_thread ().ident ,
616
- )
617
- ),
618
- )
619
-
620
- elif self .requested_state == RequestedState .StepOut and len (self .full_stack_frames ) <= self .stop_stack_len :
621
- self .state = State .Paused
622
- self .requested_state = RequestedState .Nothing
623
-
624
- self .send_event (
625
- self ,
626
- StoppedEvent (
627
- body = StoppedEventBody (
628
- reason = StoppedReason .STEP ,
629
- thread_id = threading .current_thread ().ident ,
630
- )
631
- ),
632
- )
633
-
634
604
if (
635
605
not self .terminated
636
606
and status == "FAIL"
@@ -642,8 +612,8 @@ def process_end_state(self, status: str, filter_id: Set[str], description: str,
642
612
):
643
613
reason = StoppedReason .EXCEPTION
644
614
645
- self .state = State .Paused
646
615
self .requested_state = RequestedState .Nothing
616
+ self .state = State .Paused
647
617
648
618
self .send_event (
649
619
self ,
@@ -683,6 +653,7 @@ def wait_for_running(self) -> None:
683
653
continue
684
654
685
655
if self .requested_state == RequestedState .Running :
656
+ self .requested_state = RequestedState .Nothing
686
657
self .state = State .Running
687
658
if self .main_thread is not None and self .main_thread .ident is not None :
688
659
self .send_event (
@@ -691,7 +662,6 @@ def wait_for_running(self) -> None:
691
662
body = ContinuedEventBody (thread_id = self .main_thread .ident , all_threads_continued = True )
692
663
),
693
664
)
694
- self .requested_state = RequestedState .Nothing
695
665
continue
696
666
697
667
break
@@ -938,7 +908,7 @@ def start_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
938
908
"BuiltIn.Run Keyword And Continue On Failure" ,
939
909
]
940
910
941
- def in_caughted_keyword (self ) -> bool :
911
+ def is_not_caughted_by_keyword (self ) -> bool :
942
912
r = next (
943
913
(
944
914
v
@@ -949,6 +919,56 @@ def in_caughted_keyword(self) -> bool:
949
919
)
950
920
return r is None
951
921
922
+ __matchers : Optional [Dict [str , Callable [[str , str ], bool ]]] = None
923
+
924
+ def _get_matcher (self , pattern_type : str ) -> Optional [Callable [[str , str ], bool ]]:
925
+ from robot .utils import Matcher
926
+
927
+ if self .__matchers is None :
928
+ self .__matchers : Dict [str , Callable [[str , str ], bool ]] = {
929
+ "GLOB" : lambda m , p : bool (Matcher (p , spaceless = False , caseless = False ).match (m )),
930
+ "LITERAL" : lambda m , p : m == p ,
931
+ "REGEXP" : lambda m , p : re .match (rf"{ p } \Z" , m ) is not None ,
932
+ "START" : lambda m , p : m .startswith (p ),
933
+ }
934
+
935
+ return self .__matchers .get (pattern_type .upper (), None )
936
+
937
+ def _should_run_except (self , branch : Any , error : str ) -> bool :
938
+ if not branch .patterns :
939
+ return True
940
+
941
+ if branch .pattern_type :
942
+ pattern_type = EXECUTION_CONTEXTS .current .variables .replace_string (branch .pattern_type )
943
+ else :
944
+ pattern_type = "LITERAL"
945
+
946
+ matcher = self ._get_matcher (pattern_type )
947
+
948
+ if not matcher :
949
+ return False
950
+
951
+ for pattern in branch .patterns :
952
+ if matcher (error , EXECUTION_CONTEXTS .current .variables .replace_string (pattern )):
953
+ return True
954
+
955
+ return False
956
+
957
+ def is_not_caugthed_by_except (self , message : Optional [str ]) -> bool :
958
+ if get_robot_version () >= (5 , 0 ):
959
+ from robot .running .model import Try
960
+
961
+ if not message :
962
+ return True
963
+
964
+ if EXECUTION_CONTEXTS .current .steps :
965
+ for branch in [f .data for f in reversed (EXECUTION_CONTEXTS .current .steps ) if isinstance (f .data , Try )]:
966
+ for except_branch in branch .except_branches :
967
+ if self ._should_run_except (except_branch , message ):
968
+ return False
969
+
970
+ return True
971
+
952
972
def end_keyword (self , name : str , attributes : Dict [str , Any ]) -> None :
953
973
type = attributes .get ("type" , None )
954
974
if self .debug :
@@ -957,7 +977,15 @@ def end_keyword(self, name: str, attributes: Dict[str, Any]) -> None:
957
977
if status == "FAIL" and type in ["KEYWORD" , "SETUP" , "TEARDOWN" ]:
958
978
self .process_end_state (
959
979
status ,
960
- {"failed_keyword" , * ({"uncaught_failed_keyword" } if self .in_caughted_keyword () else {})},
980
+ {
981
+ "failed_keyword" ,
982
+ * (
983
+ {"uncaught_failed_keyword" }
984
+ if self .is_not_caughted_by_keyword ()
985
+ and self .is_not_caugthed_by_except (self .last_fail_message )
986
+ else {}
987
+ ),
988
+ },
961
989
"Keyword failed." ,
962
990
f"Keyword failed: { self .last_fail_message } " if self .last_fail_message else "Keyword failed." ,
963
991
)
0 commit comments