robotcode-debugger 0.105.0__tar.gz → 0.106.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/PKG-INFO +3 -3
  2. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/pyproject.toml +2 -2
  3. robotcode_debugger-0.106.0/src/robotcode/debugger/__version__.py +1 -0
  4. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/dap_types.py +3 -0
  5. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/debugger.py +78 -30
  6. robotcode_debugger-0.106.0/src/robotcode/debugger/default_capabilities.py +48 -0
  7. robotcode_debugger-0.106.0/src/robotcode/debugger/id_manager.py +64 -0
  8. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/launcher/server.py +2 -44
  9. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/listeners.py +19 -3
  10. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/server.py +3 -45
  11. robotcode_debugger-0.105.0/src/robotcode/debugger/__version__.py +0 -1
  12. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/.gitignore +0 -0
  13. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/LICENSE.txt +0 -0
  14. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/README.md +0 -0
  15. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/__init__.py +0 -0
  16. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/cli.py +0 -0
  17. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/hooks.py +0 -0
  18. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/launcher/__init__.py +0 -0
  19. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/launcher/cli.py +0 -0
  20. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/launcher/client.py +0 -0
  21. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/launcher/run.py +0 -0
  22. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/protocol.py +0 -0
  23. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/py.typed +0 -0
  24. {robotcode_debugger-0.105.0 → robotcode_debugger-0.106.0}/src/robotcode/debugger/run.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotcode-debugger
3
- Version: 0.105.0
3
+ Version: 0.106.0
4
4
  Summary: RobotCode Debugger for Robot Framework
5
5
  Project-URL: Homepage, https://robotcode.io
6
6
  Project-URL: Donate, https://opencollective.com/robotcode
@@ -25,8 +25,8 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
25
25
  Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.8
28
- Requires-Dist: robotcode-jsonrpc2==0.105.0
29
- Requires-Dist: robotcode-runner==0.105.0
28
+ Requires-Dist: robotcode-jsonrpc2==0.106.0
29
+ Requires-Dist: robotcode-runner==0.106.0
30
30
  Requires-Dist: robotframework>=4.1.0
31
31
  Provides-Extra: debugpy
32
32
  Requires-Dist: debugpy; extra == 'debugpy'
@@ -28,8 +28,8 @@ classifiers = [
28
28
  dynamic = ["version"]
29
29
  dependencies = [
30
30
  "robotframework>=4.1.0",
31
- "robotcode-jsonrpc2==0.105.0",
32
- "robotcode-runner==0.105.0",
31
+ "robotcode-jsonrpc2==0.106.0",
32
+ "robotcode-runner==0.106.0",
33
33
  ]
34
34
 
35
35
  [project.optional-dependencies]
@@ -0,0 +1 @@
1
+ __version__ = "0.106.0"
@@ -523,6 +523,9 @@ class SourceBreakpoint(Model):
523
523
  hit_condition: Optional[str] = None
524
524
  log_message: Optional[str] = None
525
525
 
526
+ def __hash__(self) -> int:
527
+ return hash((self.line, self.column, self.condition, self.hit_condition, self.log_message))
528
+
526
529
 
527
530
  @dataclass
528
531
  class SetBreakpointsArguments(Model):
@@ -8,6 +8,7 @@ import time
8
8
  import weakref
9
9
  from collections import deque
10
10
  from enum import Enum
11
+ from functools import cached_property
11
12
  from pathlib import Path, PurePath
12
13
  from typing import (
13
14
  Any,
@@ -25,6 +26,7 @@ from typing import (
25
26
  Sequence,
26
27
  Set,
27
28
  Tuple,
29
+ TypedDict,
28
30
  Union,
29
31
  cast,
30
32
  )
@@ -67,6 +69,7 @@ from .dap_types import (
67
69
  Variable,
68
70
  VariablePresentationHint,
69
71
  )
72
+ from .id_manager import IdManager
70
73
 
71
74
  if get_robot_version() >= (7, 0):
72
75
  from robot.running import UserKeyword as UserKeywordHandler
@@ -163,6 +166,10 @@ class InvalidThreadIdError(Exception):
163
166
  super().__init__(f"Invalid thread id {thread_id}")
164
167
 
165
168
 
169
+ class MarkerObject:
170
+ pass
171
+
172
+
166
173
  class StackFrameEntry:
167
174
  def __init__(
168
175
  self,
@@ -193,10 +200,10 @@ class StackFrameEntry:
193
200
  self.libname = libname
194
201
  self.kwname = kwname
195
202
  self.longname = longname
196
- self._suite_marker = object()
197
- self._test_marker = object()
198
- self._local_marker = object()
199
- self._global_marker = object()
203
+ self._suite_marker = MarkerObject()
204
+ self._test_marker = MarkerObject()
205
+ self._local_marker = MarkerObject()
206
+ self._global_marker = MarkerObject()
200
207
  self.stack_frames: Deque[StackFrameEntry] = deque()
201
208
 
202
209
  def __repr__(self) -> str:
@@ -207,21 +214,27 @@ class StackFrameEntry:
207
214
  return self.stack_frames[0]
208
215
  return self
209
216
 
210
- @property
217
+ _id_manager = IdManager()
218
+
219
+ @cached_property
211
220
  def id(self) -> int:
212
- return id(self)
221
+ return self._id_manager.get_id(self)
213
222
 
223
+ @cached_property
214
224
  def test_id(self) -> int:
215
- return id(self._test_marker)
225
+ return self._id_manager.get_id(self._test_marker)
216
226
 
227
+ @cached_property
217
228
  def suite_id(self) -> int:
218
- return id(self._suite_marker)
229
+ return self._id_manager.get_id(self._suite_marker)
219
230
 
231
+ @cached_property
220
232
  def local_id(self) -> int:
221
- return id(self._local_marker)
233
+ return self._id_manager.get_id(self._local_marker)
222
234
 
235
+ @cached_property
223
236
  def global_id(self) -> int:
224
- return id(self._global_marker)
237
+ return self._id_manager.get_id(self._global_marker)
225
238
 
226
239
 
227
240
  class HitCountEntry(NamedTuple):
@@ -268,6 +281,15 @@ else:
268
281
  self.steps.pop()
269
282
 
270
283
 
284
+ breakpoint_id_manager = IdManager()
285
+
286
+
287
+ class ExceptionInformation(TypedDict):
288
+ text: Optional[str]
289
+ description: str
290
+ status: str
291
+
292
+
271
293
  class Debugger:
272
294
  __instance: ClassVar[Optional["Debugger"]] = None
273
295
  __lock: ClassVar = threading.RLock()
@@ -341,6 +363,7 @@ class Debugger:
341
363
  self._evaluate_cache: List[Any] = []
342
364
  self._variables_cache: Dict[int, Any] = {}
343
365
  self._variables_object_cache: List[Any] = []
366
+ self._current_exception: Optional[ExceptionInformation] = None
344
367
 
345
368
  @property
346
369
  def state(self) -> State:
@@ -432,7 +455,8 @@ class Debugger:
432
455
  self.condition.notify_all()
433
456
 
434
457
  def pause_thread(self, thread_id: int) -> None:
435
- if self.main_thread is None or thread_id != self.main_thread.ident:
458
+ # thread_id 0 means all threads
459
+ if self.main_thread is None or (thread_id != 0 and thread_id != self.main_thread.ident):
436
460
  raise InvalidThreadIdError(thread_id)
437
461
 
438
462
  with self.condition:
@@ -521,6 +545,7 @@ class Debugger:
521
545
  lines: Optional[List[int]] = None,
522
546
  source_modified: Optional[bool] = None,
523
547
  ) -> List[Breakpoint]:
548
+
524
549
  if self.is_windows_path(source.path or ""):
525
550
  path: pathlib.PurePath = pathlib.PureWindowsPath(source.path or "")
526
551
  else:
@@ -535,7 +560,7 @@ class Debugger:
535
560
  )
536
561
  return [
537
562
  Breakpoint(
538
- id=id(v),
563
+ id=breakpoint_id_manager.get_id(v),
539
564
  source=Source(path=str(path)),
540
565
  verified=True,
541
566
  line=v.line,
@@ -559,6 +584,7 @@ class Debugger:
559
584
  self,
560
585
  StoppedEvent(
561
586
  body=StoppedEventBody(
587
+ description="Paused",
562
588
  reason=StoppedReason.PAUSE,
563
589
  thread_id=threading.current_thread().ident,
564
590
  )
@@ -574,6 +600,7 @@ class Debugger:
574
600
  self,
575
601
  StoppedEvent(
576
602
  body=StoppedEventBody(
603
+ description="Next step",
577
604
  reason=StoppedReason.STEP,
578
605
  thread_id=threading.current_thread().ident,
579
606
  )
@@ -588,6 +615,7 @@ class Debugger:
588
615
  self,
589
616
  StoppedEvent(
590
617
  body=StoppedEventBody(
618
+ description="Step in",
591
619
  reason=StoppedReason.STEP,
592
620
  thread_id=threading.current_thread().ident,
593
621
  )
@@ -602,6 +630,7 @@ class Debugger:
602
630
  self,
603
631
  StoppedEvent(
604
632
  body=StoppedEventBody(
633
+ description="Step out",
605
634
  reason=StoppedReason.STEP,
606
635
  thread_id=threading.current_thread().ident,
607
636
  )
@@ -613,6 +642,7 @@ class Debugger:
613
642
  if source_path in self.breakpoints:
614
643
  breakpoints = [v for v in self.breakpoints[source_path].breakpoints if v.line == line_no]
615
644
  if len(breakpoints) > 0:
645
+
616
646
  for point in breakpoints:
617
647
  if point.condition is not None:
618
648
  hit = False
@@ -673,9 +703,10 @@ class Debugger:
673
703
  self,
674
704
  StoppedEvent(
675
705
  body=StoppedEventBody(
706
+ description="Breakpoint hit",
676
707
  reason=StoppedReason.BREAKPOINT,
677
708
  thread_id=threading.current_thread().ident,
678
- hit_breakpoint_ids=[id(v) for v in breakpoints],
709
+ hit_breakpoint_ids=[breakpoint_id_manager.get_id(v) for v in breakpoints],
679
710
  )
680
711
  ),
681
712
  )
@@ -704,6 +735,8 @@ class Debugger:
704
735
  self.requested_state = RequestedState.Nothing
705
736
  self.state = State.Paused
706
737
 
738
+ self._current_exception = {"text": text, "description": description, "status": status}
739
+
707
740
  self.send_event(
708
741
  self,
709
742
  StoppedEvent(
@@ -757,6 +790,7 @@ class Debugger:
757
790
  continue
758
791
 
759
792
  break
793
+ self._current_exception = None
760
794
 
761
795
  def start_output_group(self, name: str, attributes: Dict[str, Any], type: Optional[str] = None) -> None:
762
796
  if self.group_output:
@@ -971,7 +1005,7 @@ class Debugger:
971
1005
  if status == "FAIL":
972
1006
  self.process_end_state(
973
1007
  status,
974
- {"failed_test"},
1008
+ {""},
975
1009
  "Test failed.",
976
1010
  f"Test failed{f': {v}' if (v := attributes.get('message')) else ''}",
977
1011
  )
@@ -1136,7 +1170,7 @@ class Debugger:
1136
1170
  {"uncaught_failed_keyword"}
1137
1171
  if self.is_not_caughted_by_keyword()
1138
1172
  and self.is_not_caugthed_by_except(self.last_fail_message)
1139
- else {}
1173
+ else []
1140
1174
  ),
1141
1175
  },
1142
1176
  "Keyword failed.",
@@ -1346,7 +1380,7 @@ class Debugger:
1346
1380
  name="Local",
1347
1381
  expensive=False,
1348
1382
  presentation_hint="local",
1349
- variables_reference=entry.local_id(),
1383
+ variables_reference=entry.local_id,
1350
1384
  )
1351
1385
  )
1352
1386
  if context.variables._test is not None and entry.type == "KEYWORD":
@@ -1355,7 +1389,7 @@ class Debugger:
1355
1389
  name="Test",
1356
1390
  expensive=False,
1357
1391
  presentation_hint="test",
1358
- variables_reference=entry.test_id(),
1392
+ variables_reference=entry.test_id,
1359
1393
  )
1360
1394
  )
1361
1395
  if context.variables._suite is not None and entry.type in [
@@ -1367,7 +1401,7 @@ class Debugger:
1367
1401
  name="Suite",
1368
1402
  expensive=False,
1369
1403
  presentation_hint="suite",
1370
- variables_reference=entry.suite_id(),
1404
+ variables_reference=entry.suite_id,
1371
1405
  )
1372
1406
  )
1373
1407
  if context.variables._global is not None:
@@ -1376,20 +1410,24 @@ class Debugger:
1376
1410
  name="Global",
1377
1411
  expensive=False,
1378
1412
  presentation_hint="global",
1379
- variables_reference=entry.global_id(),
1413
+ variables_reference=entry.global_id,
1380
1414
  )
1381
1415
  )
1382
1416
 
1383
1417
  return result
1384
1418
 
1419
+ _cache_id_manager = IdManager()
1420
+
1385
1421
  def _new_cache_id(self) -> int:
1386
- o = object()
1422
+ o = MarkerObject()
1387
1423
  self._variables_object_cache.append(o)
1388
- return id(o)
1424
+ return StackFrameEntry._id_manager.get_id(o)
1389
1425
 
1390
1426
  debug_repr = DebugRepr()
1391
1427
 
1392
- def _create_variable(self, name: str, value: Any) -> Variable:
1428
+ def _create_variable(
1429
+ self, name: str, value: Any, presentation_hint: Optional[VariablePresentationHint] = None
1430
+ ) -> Variable:
1393
1431
  if isinstance(value, Mapping):
1394
1432
  v_id = self._new_cache_id()
1395
1433
  self._variables_cache[v_id] = value
@@ -1400,7 +1438,9 @@ class Debugger:
1400
1438
  variables_reference=v_id,
1401
1439
  named_variables=len(value) + 1,
1402
1440
  indexed_variables=0,
1403
- presentation_hint=VariablePresentationHint(kind="data"),
1441
+ presentation_hint=(
1442
+ presentation_hint if presentation_hint is not None else VariablePresentationHint(kind="data")
1443
+ ),
1404
1444
  )
1405
1445
 
1406
1446
  if isinstance(value, Sequence) and not isinstance(value, str):
@@ -1443,18 +1483,18 @@ class Debugger:
1443
1483
  (
1444
1484
  v
1445
1485
  for v in self.stack_frames
1446
- if variables_reference in [v.global_id(), v.suite_id(), v.test_id(), v.local_id()]
1486
+ if variables_reference in [v.global_id, v.suite_id, v.test_id, v.local_id]
1447
1487
  ),
1448
1488
  None,
1449
1489
  )
1450
1490
  if entry is not None:
1451
1491
  context = entry.context()
1452
1492
  if context is not None:
1453
- if entry.global_id() == variables_reference:
1493
+ if entry.global_id == variables_reference:
1454
1494
  result.update(
1455
1495
  {k: self._create_variable(k, v) for k, v in context.variables._global.as_dict().items()}
1456
1496
  )
1457
- elif entry.suite_id() == variables_reference:
1497
+ elif entry.suite_id == variables_reference:
1458
1498
  globals = context.variables._global.as_dict()
1459
1499
  vars = entry.get_first_or_self().variables()
1460
1500
  vars_dict = vars.as_dict() if vars is not None else {}
@@ -1465,7 +1505,7 @@ class Debugger:
1465
1505
  if (k not in globals or globals[k] != v) and (k in vars_dict)
1466
1506
  }
1467
1507
  )
1468
- elif entry.test_id() == variables_reference:
1508
+ elif entry.test_id == variables_reference:
1469
1509
  globals = context.variables._suite.as_dict()
1470
1510
  vars = entry.get_first_or_self().variables()
1471
1511
  vars_dict = vars.as_dict() if vars is not None else {}
@@ -1476,8 +1516,16 @@ class Debugger:
1476
1516
  if (k not in globals or globals[k] != v) and (k in vars_dict)
1477
1517
  }
1478
1518
  )
1479
- elif entry.local_id() == variables_reference:
1519
+ elif entry.local_id == variables_reference:
1480
1520
  vars = entry.get_first_or_self().variables()
1521
+
1522
+ if self._current_exception is not None:
1523
+ result["${EXCEPTION}"] = self._create_variable(
1524
+ "${EXCEPTION}",
1525
+ self._current_exception,
1526
+ VariablePresentationHint(kind="virtual"),
1527
+ )
1528
+
1481
1529
  if vars is not None:
1482
1530
  p = entry.parent() if entry.parent else None
1483
1531
 
@@ -1821,7 +1869,7 @@ class Debugger:
1821
1869
  (
1822
1870
  v
1823
1871
  for v in self.full_stack_frames
1824
- if variables_reference in [v.global_id(), v.local_id(), v.suite_id(), v.test_id()]
1872
+ if variables_reference in [v.global_id, v.local_id, v.suite_id, v.test_id]
1825
1873
  ),
1826
1874
  None,
1827
1875
  )
@@ -1856,7 +1904,7 @@ class Debugger:
1856
1904
  if option.filter_id in [
1857
1905
  "failed_keyword",
1858
1906
  "uncaught_failed_keyword",
1859
- "failed_test",
1907
+ "",
1860
1908
  "failed_suite",
1861
1909
  ]:
1862
1910
  entry = ExceptionBreakpointsEntry(
@@ -0,0 +1,48 @@
1
+ from .dap_types import Capabilities, ExceptionBreakpointsFilter
2
+
3
+ DFEAULT_CAPABILITIES = Capabilities(
4
+ supports_configuration_done_request=True,
5
+ supports_conditional_breakpoints=True,
6
+ supports_hit_conditional_breakpoints=True,
7
+ support_terminate_debuggee=True,
8
+ supports_evaluate_for_hovers=True,
9
+ supports_terminate_request=True,
10
+ supports_log_points=True,
11
+ supports_set_expression=True,
12
+ supports_set_variable=True,
13
+ supports_value_formatting_options=True,
14
+ exception_breakpoint_filters=[
15
+ ExceptionBreakpointsFilter(
16
+ filter="failed_keyword",
17
+ label="Failed Keywords",
18
+ description="Breaks on failed keywords",
19
+ default=False,
20
+ supports_condition=True,
21
+ ),
22
+ ExceptionBreakpointsFilter(
23
+ filter="uncaught_failed_keyword",
24
+ label="Uncaught Failed Keywords",
25
+ description="Breaks on uncaught failed keywords",
26
+ default=True,
27
+ supports_condition=True,
28
+ ),
29
+ ExceptionBreakpointsFilter(
30
+ filter="failed_test",
31
+ label="Failed Test",
32
+ description="Breaks on failed tests",
33
+ default=False,
34
+ supports_condition=True,
35
+ ),
36
+ ExceptionBreakpointsFilter(
37
+ filter="failed_suite",
38
+ label="Failed Suite",
39
+ description="Breaks on failed suite",
40
+ default=False,
41
+ supports_condition=True,
42
+ ),
43
+ ],
44
+ supports_exception_options=True,
45
+ supports_exception_filter_options=True,
46
+ supports_completions_request=True,
47
+ supports_a_n_s_i_styling=True,
48
+ )
@@ -0,0 +1,64 @@
1
+ import weakref
2
+ from collections import deque
3
+ from typing import Any, Deque, Dict, Optional
4
+
5
+
6
+ class IdManager:
7
+ def __init__(self) -> None:
8
+ self.max_id: int = 2**31 - 1
9
+ self.next_id: int = 0
10
+
11
+ self.released_ids: Deque[int] = deque()
12
+ self.object_to_id: weakref.WeakKeyDictionary[Any, int] = weakref.WeakKeyDictionary()
13
+ self.id_to_object: weakref.WeakValueDictionary[int, Any] = weakref.WeakValueDictionary()
14
+ self._finalizers: Dict[int, weakref.ref[Any]] = {}
15
+
16
+ def get_id(self, obj: Any) -> int:
17
+ if obj in self.object_to_id:
18
+ return self.object_to_id[obj]
19
+
20
+ if self.released_ids:
21
+ obj_id: int = self.released_ids.popleft()
22
+ else:
23
+ if self.next_id > self.max_id:
24
+ raise RuntimeError("Keine IDs mehr verfügbar!")
25
+ obj_id = self.next_id
26
+ self.next_id += 1
27
+
28
+ self.object_to_id[obj] = obj_id
29
+ self.id_to_object[obj_id] = obj
30
+
31
+ def _on_object_gc(ref: "weakref.ReferenceType[Any]", id_: int = obj_id) -> None:
32
+ self.release_id(id_)
33
+
34
+ ref = weakref.ref(obj, _on_object_gc)
35
+ self._finalizers[obj_id] = ref
36
+
37
+ return obj_id
38
+
39
+ def release_id(self, obj_id: int) -> None:
40
+ if obj_id in self.id_to_object:
41
+ del self.id_to_object[obj_id]
42
+
43
+ if obj_id in self._finalizers:
44
+ del self._finalizers[obj_id]
45
+
46
+ self.released_ids.append(obj_id)
47
+
48
+ def release_obj(self, obj: Any) -> None:
49
+ if obj in self.object_to_id:
50
+ obj_id: int = self.object_to_id.pop(obj)
51
+
52
+ if obj_id in self.id_to_object:
53
+ del self.id_to_object[obj_id]
54
+
55
+ if obj_id in self._finalizers:
56
+ del self._finalizers[obj_id]
57
+
58
+ self.released_ids.append(obj_id)
59
+
60
+ def get_object(self, obj_id: int) -> Optional[Any]:
61
+ return self.id_to_object.get(obj_id)
62
+
63
+ def get_id_from_obj(self, obj: Any) -> Optional[int]:
64
+ return self.object_to_id.get(obj)
@@ -23,7 +23,6 @@ from ..dap_types import (
23
23
  DisconnectArguments,
24
24
  DisconnectRequest,
25
25
  Event,
26
- ExceptionBreakpointsFilter,
27
26
  InitializeRequest,
28
27
  InitializeRequestArguments,
29
28
  LaunchRequestArguments,
@@ -39,6 +38,7 @@ from ..dap_types import (
39
38
  TerminatedEvent,
40
39
  TerminateRequest,
41
40
  )
41
+ from ..default_capabilities import DFEAULT_CAPABILITIES
42
42
  from ..protocol import DebugAdapterProtocol
43
43
  from .client import DAPClient, DAPClientError
44
44
 
@@ -91,49 +91,7 @@ class LauncherDebugAdapterProtocol(DebugAdapterProtocol):
91
91
 
92
92
  self._initialized = True
93
93
 
94
- return Capabilities(
95
- supports_configuration_done_request=True,
96
- supports_conditional_breakpoints=True,
97
- supports_hit_conditional_breakpoints=True,
98
- support_terminate_debuggee=True,
99
- # support_suspend_debuggee=True,
100
- supports_evaluate_for_hovers=True,
101
- supports_terminate_request=True,
102
- supports_log_points=True,
103
- supports_set_expression=True,
104
- supports_set_variable=True,
105
- supports_value_formatting_options=True,
106
- exception_breakpoint_filters=[
107
- ExceptionBreakpointsFilter(
108
- filter="failed_keyword",
109
- label="Failed Keywords",
110
- description="Breaks on failed keywords",
111
- default=False,
112
- ),
113
- ExceptionBreakpointsFilter(
114
- filter="uncaught_failed_keyword",
115
- label="Uncaught Failed Keywords",
116
- description="Breaks on uncaught failed keywords",
117
- default=True,
118
- ),
119
- ExceptionBreakpointsFilter(
120
- filter="failed_test",
121
- label="Failed Test",
122
- description="Breaks on failed tests",
123
- default=False,
124
- ),
125
- ExceptionBreakpointsFilter(
126
- filter="failed_suite",
127
- label="Failed Suite",
128
- description="Breaks on failed suite",
129
- default=False,
130
- ),
131
- ],
132
- supports_exception_options=True,
133
- supports_exception_filter_options=True,
134
- supports_completions_request=True,
135
- supports_a_n_s_i_styling=True,
136
- )
94
+ return DFEAULT_CAPABILITIES
137
95
 
138
96
  @rpc_method(name="launch", param_type=LaunchRequestArguments)
139
97
  async def _launch(
@@ -16,6 +16,8 @@ from .debugger import Debugger
16
16
  class RobotExecutionEventBody(Model):
17
17
  type: str
18
18
  id: str
19
+ name: str
20
+ parent_id: Optional[str] = None
19
21
  attributes: Optional[Dict[str, Any]] = None
20
22
  failed_keywords: Optional[List[Dict[str, Any]]] = None
21
23
 
@@ -34,15 +36,19 @@ class ListenerV2:
34
36
  def __init__(self) -> None:
35
37
  self.failed_keywords: Optional[List[Dict[str, Any]]] = None
36
38
  self.last_fail_message: Optional[str] = None
39
+ self.suite_id_stack: List[str] = []
37
40
 
38
41
  def start_suite(self, name: str, attributes: Dict[str, Any]) -> None:
42
+ id = f"{source_from_attributes(attributes)};{attributes.get('longname', '')}"
39
43
  Debugger.instance().send_event(
40
44
  self,
41
45
  Event(
42
46
  event="robotStarted",
43
47
  body=RobotExecutionEventBody(
44
48
  type="suite",
45
- id=f"{source_from_attributes(attributes)};{attributes.get('longname', '')}",
49
+ name=name,
50
+ id=id,
51
+ parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
46
52
  attributes=dict(attributes),
47
53
  ),
48
54
  ),
@@ -51,8 +57,10 @@ class ListenerV2:
51
57
  Debugger.instance().start_output_group(name, attributes, "SUITE")
52
58
 
53
59
  Debugger.instance().start_suite(name, attributes)
60
+ self.suite_id_stack.append(id)
54
61
 
55
62
  def end_suite(self, name: str, attributes: Dict[str, Any]) -> None:
63
+ id = f"{source_from_attributes(attributes)};{attributes.get('longname', '')}"
56
64
  Debugger.instance().end_suite(name, attributes)
57
65
 
58
66
  Debugger.instance().end_output_group(name, attributes, "SUITE")
@@ -63,13 +71,15 @@ class ListenerV2:
63
71
  event="robotEnded",
64
72
  body=RobotExecutionEventBody(
65
73
  type="suite",
74
+ name=name,
66
75
  attributes=dict(attributes),
67
- id=f"{source_from_attributes(attributes)};{attributes.get('longname', '')}",
76
+ id=id,
77
+ parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
68
78
  failed_keywords=self.failed_keywords,
69
79
  ),
70
80
  ),
71
81
  )
72
-
82
+ self.suite_id_stack.pop()
73
83
  self.failed_keywords = None
74
84
 
75
85
  def start_test(self, name: str, attributes: Dict[str, Any]) -> None:
@@ -81,8 +91,10 @@ class ListenerV2:
81
91
  event="robotStarted",
82
92
  body=RobotExecutionEventBody(
83
93
  type="test",
94
+ name=name,
84
95
  id=f"{source_from_attributes(attributes)};{attributes.get('longname', '')};"
85
96
  f"{attributes.get('lineno', 0)}",
97
+ parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
86
98
  attributes=dict(attributes),
87
99
  ),
88
100
  ),
@@ -103,8 +115,10 @@ class ListenerV2:
103
115
  event="robotEnded",
104
116
  body=RobotExecutionEventBody(
105
117
  type="test",
118
+ name=name,
106
119
  id=f"{source_from_attributes(attributes)};{attributes.get('longname', '')};"
107
120
  f"{attributes.get('lineno', 0)}",
121
+ parent_id=self.suite_id_stack[-1] if self.suite_id_stack else None,
108
122
  attributes=dict(attributes),
109
123
  failed_keywords=self.failed_keywords,
110
124
  ),
@@ -308,6 +322,7 @@ class ListenerV3:
308
322
  event="robotSetFailed",
309
323
  body=RobotExecutionEventBody(
310
324
  type="test",
325
+ name=result_item.name,
311
326
  attributes={
312
327
  "longname": result_item.longname,
313
328
  "status": str(result_item.status),
@@ -325,6 +340,7 @@ class ListenerV3:
325
340
  ),
326
341
  ),
327
342
  )
343
+
328
344
  if isinstance(result_item, result.TestSuite):
329
345
  for r in result_item.suites:
330
346
  p = next((i for i in data_item.suites if i.id == r.id), None) if data_item else None
@@ -23,7 +23,6 @@ from .dap_types import (
23
23
  EvaluateArguments,
24
24
  EvaluateResponseBody,
25
25
  Event,
26
- ExceptionBreakpointsFilter,
27
26
  ExitedEvent,
28
27
  ExitedEventBody,
29
28
  InitializedEvent,
@@ -50,6 +49,7 @@ from .dap_types import (
50
49
  VariablesResponseBody,
51
50
  )
52
51
  from .debugger import Debugger, PathMapping
52
+ from .default_capabilities import DFEAULT_CAPABILITIES
53
53
  from .protocol import DebugAdapterProtocol
54
54
 
55
55
  TCP_DEFAULT_PORT = 6612
@@ -132,7 +132,7 @@ class DebugAdapterServerProtocol(DebugAdapterProtocol):
132
132
  return self._initialized
133
133
 
134
134
  @_logger.call
135
- def wait_for_disconnected(self, timeout: float = 15) -> bool:
135
+ def wait_for_disconnected(self, timeout: float = 1) -> bool:
136
136
  self._disconnected_event.wait(timeout)
137
137
 
138
138
  return not self._connected
@@ -144,49 +144,7 @@ class DebugAdapterServerProtocol(DebugAdapterProtocol):
144
144
  if self.loop is not None:
145
145
  self.loop.call_soon(self.initialized)
146
146
 
147
- return Capabilities(
148
- supports_configuration_done_request=True,
149
- supports_conditional_breakpoints=True,
150
- supports_hit_conditional_breakpoints=True,
151
- support_terminate_debuggee=True,
152
- # support_suspend_debuggee=True,
153
- supports_evaluate_for_hovers=True,
154
- supports_terminate_request=True,
155
- supports_log_points=True,
156
- supports_set_expression=True,
157
- supports_set_variable=True,
158
- supports_value_formatting_options=True,
159
- exception_breakpoint_filters=[
160
- ExceptionBreakpointsFilter(
161
- filter="failed_keyword",
162
- label="Failed Keywords",
163
- description="Breaks on failed keywords",
164
- default=False,
165
- ),
166
- ExceptionBreakpointsFilter(
167
- filter="uncaught_failed_keyword",
168
- label="Uncaught Failed Keywords",
169
- description="Breaks on uncaught failed keywords",
170
- default=True,
171
- ),
172
- ExceptionBreakpointsFilter(
173
- filter="failed_test",
174
- label="Failed Test",
175
- description="Breaks on failed tests",
176
- default=False,
177
- ),
178
- ExceptionBreakpointsFilter(
179
- filter="failed_suite",
180
- label="Failed Suite",
181
- description="Breaks on failed suite",
182
- default=False,
183
- ),
184
- ],
185
- supports_exception_options=True,
186
- supports_exception_filter_options=True,
187
- supports_completions_request=True,
188
- supports_a_n_s_i_styling=True,
189
- )
147
+ return DFEAULT_CAPABILITIES
190
148
 
191
149
  @rpc_method(name="attach", param_type=AttachRequestArguments)
192
150
  async def _attach(
@@ -1 +0,0 @@
1
- __version__ = "0.105.0"