From c72176d029299ba74d32fd8da6db683c99959376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 19 Apr 2026 10:05:40 +0200 Subject: [PATCH] Improve loading error messages with item context and hierarchy path Add item_load_context() context manager to tum_except.py that enriches ETUMSyntaxError with the item type, name, and parent path instead of replacing the original message with a vague 'missing or wrong parameter'. Co-Authored-By: Claude Sonnet 4.6 --- src/lib/tum_except.py | 23 +++++++++++++ .../interpreter/test_items/test_item.py | 14 ++++---- .../interpreter/test_items/test_item_check.py | 11 ++---- .../test_items/test_item_choices_dialog.py | 13 ++----- .../test_items/test_item_image_dialog.py | 9 ++--- .../interpreter/test_items/test_item_let.py | 11 ++---- .../test_items/test_item_lua_func.py | 9 ++--- .../test_items/test_item_msg_dialog.py | 11 ++---- .../test_items/test_item_note_dialog.py | 11 ++---- .../test_items/test_item_py_func.py | 9 ++--- .../test_items/test_item_question_dialog.py | 11 ++---- .../interpreter/test_items/test_item_run.py | 9 ++--- .../test_items/test_item_runtime_plot.py | 9 ++--- .../interpreter/test_items/test_item_sleep.py | 11 ++---- .../test_items/test_item_tested_references.py | 9 ++--- .../test_items/test_item_value_dialog.py | 11 ++---- src/testium/interpreter/test_set.py | 34 ++++++++++++++----- 17 files changed, 92 insertions(+), 123 deletions(-) diff --git a/src/lib/tum_except.py b/src/lib/tum_except.py index ef875bf..cdaccee 100644 --- a/src/lib/tum_except.py +++ b/src/lib/tum_except.py @@ -1,5 +1,6 @@ import traceback import textwrap +from contextlib import contextmanager class ETUMError(Exception): @@ -67,6 +68,28 @@ class ETUMParamError(ETUMError): return lines +@contextmanager +def item_load_context(item_type: str, item_name: str, filename: str = ""): + """Context manager that enriches ETUMSyntaxError with item context during loading. + + Usage in test item __init__: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self.param = self._prms.getParam("param", required=True) + """ + try: + yield + except ETUMSyntaxError as e: + raise ETUMSyntaxError( + f"In '{item_type}' item named '{item_name}':\n{e._message}", + filename or e._file, + ) from e + except Exception as e: + raise ETUMSyntaxError( + f"In '{item_type}' item named '{item_name}':\nUnexpected error: {e}", + filename, + ) from e + + def print_exception(exc: ETUMError): if not isinstance(exc, ETUMError): print(traceback.format_exc(4)) diff --git a/src/testium/interpreter/test_items/test_item.py b/src/testium/interpreter/test_items/test_item.py index abd9ba8..4d1c852 100644 --- a/src/testium/interpreter/test_items/test_item.py +++ b/src/testium/interpreter/test_items/test_item.py @@ -7,7 +7,7 @@ import libs.testium as tm from interpreter.utils.params import TestItemParams from interpreter.utils.constants import TestItemType as cst_type from interpreter.utils.eval import eval_to_boolean, evaluate, post_evaluate -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context LOG_TEST_STOP = '<----- step "{}" finished' LOG_TEST_START = '-----> step "{}" started' @@ -131,11 +131,11 @@ class TestItem: if s: try: self.skipped = eval_to_boolean(s) - except: + except Exception as e: raise ETUMSyntaxError( f"'{self.cmd()}' test item named '{self.name()}':\nskipped expresion can only be a static expression as it is evaluated during loading of TUM : {s}", self.seqFilename(), - ) + ) from e # This allow disabling test item directly by using its name inside param.yaml file elif self._name in tm.gd("skipped_test_item", []): self.skipped = True @@ -164,11 +164,13 @@ class TestItem: self.banner = LOG_TEST_START.format(self._name) self.footer = LOG_TEST_STOP.format(self._name) - except: + except ETUMSyntaxError: + raise + except Exception as e: raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", + f"The '{self.cmd()}' test item named '{self.name()}' has an unexpected loading error: {e}", self.seqFilename(), - ) + ) from e self.result = TestResult(self, TestValue.FAILURE, "Failure by default") diff --git a/src/testium/interpreter/test_items/test_item_check.py b/src/testium/interpreter/test_items/test_item_check.py index c0c9597..e30b2d6 100644 --- a/src/testium/interpreter/test_items/test_item_check.py +++ b/src/testium/interpreter/test_items/test_item_check.py @@ -1,7 +1,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import TestValue -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context import libs.testium as tm from interpreter.utils.constants import TestItemType as cst from interpreter.utils.eval import evaluate @@ -15,21 +15,16 @@ class TestItemCheckValue(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_CHECK self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self._action_list = self._prms.getParamAll('steps', default=[], required=False) if len(self._action_list) > 0: tm.print_warn("'steps' argument of check test item is deprecated and is replaced by 'values'") self._action_list += self._prms.getParamAll('values', default=[], required=False) if len(self._action_list) <= 0: raise ETUMSyntaxError( - f" The '{self.cmd()}' test item named '{self.name()}' must have a 'values' parameter", + f"Missing required 'values' parameter", self.seqFilename() ) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' (a child of: '{self.parent().name()}') has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_choices_dialog.py b/src/testium/interpreter/test_items/test_item_choices_dialog.py index 77c9029..ad1fa4b 100644 --- a/src/testium/interpreter/test_items/test_item_choices_dialog.py +++ b/src/testium/interpreter/test_items/test_item_choices_dialog.py @@ -4,7 +4,7 @@ from interpreter.test_items.test_item import TestItem, test_run from interpreter.test_items.test_result import TestResult, TestValue from interpreter.test_items.dialog_choices_files import choices_dialog import libs.testium as tm -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context from interpreter.utils.constants import TestItemType as cst @@ -14,17 +14,10 @@ class TestItemChoicesDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_CHOICES_DLG self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self._question = self._prms.getParam("question", required=True) self._choices = self._prms.getParam("choices", required=True) - self._default_icon = self._prms.getParam( - "icon", required=False, default=None - ) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' (a child of: '{self.parent().name()}') has a missing or wrong parameter", - self.seqFilename() - ) + self._default_icon = self._prms.getParam("icon", required=False, default=None) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_image_dialog.py b/src/testium/interpreter/test_items/test_item_image_dialog.py index 9a3b96d..da32805 100644 --- a/src/testium/interpreter/test_items/test_item_image_dialog.py +++ b/src/testium/interpreter/test_items/test_item_image_dialog.py @@ -7,7 +7,7 @@ from interpreter.test_items.test_result import TestResult, TestValue from interpreter.test_items.dialog_image_files import dialog_image import libs.testium as tm from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context class TestItemImageDialog(TestItem): @@ -20,14 +20,9 @@ class TestItemImageDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_IMAGE_DLG self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self._question = self._prms.getParam("question", required=True) self._filename = self._prms.getParam("filename", required=True) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_let.py b/src/testium/interpreter/test_items/test_item_let.py index 9f42dd2..49f3dc1 100644 --- a/src/testium/interpreter/test_items/test_item_let.py +++ b/src/testium/interpreter/test_items/test_item_let.py @@ -5,7 +5,7 @@ import time from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestResult, TestValue) -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context import libs.testium as tm from interpreter.utils.constants import TestItemType as cst @@ -19,18 +19,13 @@ class TestItemLet(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_LET self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self._values_list = self._prms.getParamAll('values', default=[], required=False) if len(self._values_list) <= 0: raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' must have a 'values' parameter", + f"Missing required 'values' parameter", self.seqFilename(), ) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_lua_func.py b/src/testium/interpreter/test_items/test_item_lua_func.py index 078362f..0bbb443 100644 --- a/src/testium/interpreter/test_items/test_item_lua_func.py +++ b/src/testium/interpreter/test_items/test_item_lua_func.py @@ -4,7 +4,7 @@ import traceback import pprint import textwrap -from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError +from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context from interpreter.test_items.test_item import TestItem, test_run from interpreter.test_items.test_result import TestValue import libs.testium as tm @@ -26,16 +26,11 @@ class TestItemLuaFunc(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_LUA_FUNCTION self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self.file_name = self._prms.getParam("file", required=True) self.func_name = self._prms.getParam("func_name", required=True) self.params = self._prms.getParamAll("param") self._context_id = self._prms.getParam("context_id", default=None, processed=False) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' (child of '{self.parent.name()}') has a missing or wrong parameter", - self.seqFilename(), - ) self._lua_func_proc = LuaFuncExecEngine(tm.gd("lua_bin", ""), api_request, 10) def _get_engine(self): diff --git a/src/testium/interpreter/test_items/test_item_msg_dialog.py b/src/testium/interpreter/test_items/test_item_msg_dialog.py index b0b7fb4..cd41265 100644 --- a/src/testium/interpreter/test_items/test_item_msg_dialog.py +++ b/src/testium/interpreter/test_items/test_item_msg_dialog.py @@ -6,7 +6,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestValue) from interpreter.test_items.dialog_msg_files import msg_dialog from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context class TestItemMsgDialog(TestItem): """dialog_message item usage. @@ -17,13 +17,8 @@ class TestItemMsgDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_MESSAGE_DLG self.is_container = False - try: - self._question = self._prms.getParam('question', required = True) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_note_dialog.py b/src/testium/interpreter/test_items/test_item_note_dialog.py index dbded8d..4259dfd 100644 --- a/src/testium/interpreter/test_items/test_item_note_dialog.py +++ b/src/testium/interpreter/test_items/test_item_note_dialog.py @@ -5,7 +5,7 @@ from multiprocessing import Process, Pipe from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestResult, TestValue) from interpreter.test_items.dialog_note_files import test_dialog -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context import libs.testium as tm from interpreter.utils.constants import TestItemType as cst @@ -15,13 +15,8 @@ class TestItemNoteDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_NOTE_DLG self.is_container = False - try: - self._question = self._prms.getParam('question', required = True) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_py_func.py b/src/testium/interpreter/test_items/test_item_py_func.py index 1d8ffee..e560d49 100644 --- a/src/testium/interpreter/test_items/test_item_py_func.py +++ b/src/testium/interpreter/test_items/test_item_py_func.py @@ -4,7 +4,7 @@ import time import pprint import textwrap -from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError +from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context from interpreter.test_items.test_item import TestItem, test_run from interpreter.test_items.test_result import TestValue import libs.testium as tm @@ -26,16 +26,11 @@ class TestItemPyFunc(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_PY_FUNCTION self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self.file_name = self._prms.getParam("file", required=True) self.func_name = self._prms.getParam("func_name", required=True) self.params = self._prms.getParamAll("param") self._context_id = self._prms.getParam("context_id", default=None, processed=False) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' (child of '{self.parent.name()}') has a missing or wrong parameter", - self.seqFilename(), - ) self._py_func_proc = PyFuncExecEngine(tm.gd("python_bin", ""), api_request, 10) def _get_engine(self): diff --git a/src/testium/interpreter/test_items/test_item_question_dialog.py b/src/testium/interpreter/test_items/test_item_question_dialog.py index 7a04995..8e7bd5a 100644 --- a/src/testium/interpreter/test_items/test_item_question_dialog.py +++ b/src/testium/interpreter/test_items/test_item_question_dialog.py @@ -7,7 +7,7 @@ from PySide6.QtWidgets import QMessageBox from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestResult, TestValue) from interpreter.test_items.dialog_question_files import question_dialog -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context from interpreter.utils.constants import TestItemType as cst class TestItemQuestionDialog(TestItem): @@ -19,13 +19,8 @@ class TestItemQuestionDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_QUESTION_DLG self.is_container = False - try: - self._question = self._prms.getParam('question', required = True) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_run.py b/src/testium/interpreter/test_items/test_item_run.py index 7b92e91..59a45ee 100644 --- a/src/testium/interpreter/test_items/test_item_run.py +++ b/src/testium/interpreter/test_items/test_item_run.py @@ -10,7 +10,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestValue) import libs.testium as tm from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError +from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context def nowInBetween(start, end): @@ -30,7 +30,7 @@ class TestItemRun(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_RUN self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self.tum_fime = self._prms.getParam('tum_fime', required=True) self.param_file = self._prms.getParam('param_file', default='') self.python_bin = self._prms.getParam('python_bin', default='') @@ -40,11 +40,6 @@ class TestItemRun(TestItem): self.start_time = self._prms.getParam('start_time') self.end_time = self._prms.getParam('end_time') self.wait_for_exec = self._prms.getParam('wait_for_exec') - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_runtime_plot.py b/src/testium/interpreter/test_items/test_item_runtime_plot.py index bcc8cde..8415f78 100644 --- a/src/testium/interpreter/test_items/test_item_runtime_plot.py +++ b/src/testium/interpreter/test_items/test_item_runtime_plot.py @@ -4,7 +4,7 @@ import traceback from functools import wraps import libs.testium as tm -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context from interpreter.test_items.test_item import TestItem, test_run from interpreter.test_items.test_result import TestResult, TestValue from interpreter.test_items.item_actions import TestItemActions @@ -108,17 +108,12 @@ class TestItemPlotActionPeriodic(TestItemPlotAction): ) # Periodic function call - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self.period = self._prms.getParam("period", required=True) self.file_name = self._prms.getParam("file", required=True) self.func_name = self._prms.getParam("func_name", required=True) self.params = self._prms.getParamAll("param") self.post_eval = self._prms.getParam("eval", default="") - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' 'periodic' action settings syntax error", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_sleep.py b/src/testium/interpreter/test_items/test_item_sleep.py index dd22afb..1b6a15d 100644 --- a/src/testium/interpreter/test_items/test_item_sleep.py +++ b/src/testium/interpreter/test_items/test_item_sleep.py @@ -7,7 +7,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestValue) from interpreter.test_items.dialog_sleep_files import dialog_sleep from interpreter.utils.constants import TestItemType as cst -from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError +from lib.tum_except import ETUMSyntaxError, ETUMRuntimeError, item_load_context class TestItemSleep(TestItem): """sleep item usage. @@ -19,14 +19,9 @@ class TestItemSleep(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_SLEEP self.is_container = False - try: - self._timeout = self._prms.getParam('timeout', required = True) + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._timeout = self._prms.getParam('timeout', required=True) self._has_dialog = self._prms.getParam('dialog', default=False) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_tested_references.py b/src/testium/interpreter/test_items/test_item_tested_references.py index e39ad54..9345206 100644 --- a/src/testium/interpreter/test_items/test_item_tested_references.py +++ b/src/testium/interpreter/test_items/test_item_tested_references.py @@ -6,7 +6,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestResult, TestValue) from interpreter.test_items.tested_references_files import tested_refs_dialog import libs.testium as tm -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context from interpreter.utils.constants import TestItemType as cst class TestItemTestedRefsDialog(TestItem): @@ -15,14 +15,9 @@ class TestItemTestedRefsDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_REFERENCE_DLG self.is_container = False - try: + with item_load_context(self.cmd(), self.name(), self.seqFilename()): self._question = self._prms.getParam('question', required=True) self._init_values = self._prms.getParamAll('reference', required=False, processed=True) - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_items/test_item_value_dialog.py b/src/testium/interpreter/test_items/test_item_value_dialog.py index c38ece7..027fc3f 100644 --- a/src/testium/interpreter/test_items/test_item_value_dialog.py +++ b/src/testium/interpreter/test_items/test_item_value_dialog.py @@ -6,7 +6,7 @@ from interpreter.test_items.test_item import (TestItem, test_run) from interpreter.test_items.test_result import (TestResult, TestValue) from interpreter.test_items.dialog_value_files import test_dialog import libs.testium as tm -from lib.tum_except import ETUMSyntaxError +from lib.tum_except import ETUMSyntaxError, item_load_context from interpreter.utils.constants import TestItemType as cst class TestItemValueDialog(TestItem): @@ -18,14 +18,9 @@ class TestItemValueDialog(TestItem): super().__init__(dict_item, parent, status_queue, filename=filename) self._type = cst.TYPE_VALUE_DLG self.is_container = False - try: - self._question = self._prms.getParam('question', required = True) + with item_load_context(self.cmd(), self.name(), self.seqFilename()): + self._question = self._prms.getParam('question', required=True) self._default = self._prms.getParam('default', '') - except: - raise ETUMSyntaxError( - f"The '{self.cmd()}' test item named '{self.name()}' has a missing or wrong parameter", - self.seqFilename(), - ) @test_run def execute(self): diff --git a/src/testium/interpreter/test_set.py b/src/testium/interpreter/test_set.py index 4c97229..93ae79a 100644 --- a/src/testium/interpreter/test_set.py +++ b/src/testium/interpreter/test_set.py @@ -3,9 +3,7 @@ import datetime from queue import Queue from interpreter.utils.params import expanse import libs.testium as tm -from lib.tum_except import ( - ETUMSyntaxError, -) +from lib.tum_except import ETUMSyntaxError import interpreter.utils.settings as prefs from interpreter.test_report.test_report import TestReport from interpreter.utils.py_func_exec import PyFuncExecEngine @@ -19,6 +17,17 @@ from interpreter.test_items.item_actions import TestItemActions from interpreter.test_items.test_result import TestValue +def _build_item_path(item) -> str: + """Build a breadcrumb path like 'main > Group > sub-group' from an item to root.""" + parts = [] + current = item + while current is not None: + name = current.name() + parts.append(name if name else f"[{current.type()}]") + current = current.parent() + return " > ".join(reversed(parts)) + + class TestSet: def __init__( self, @@ -479,12 +488,19 @@ class TestSet: action_name = cst.FOLDED_CHAR + it.item_cmd seq_filename = action[action_name]["seq_filename"] - item = (it.item_class)( - action[action_name], - tree_parent, - self.status_queue, - filename=seq_filename - ) + try: + item = (it.item_class)( + action[action_name], + tree_parent, + self.status_queue, + filename=seq_filename + ) + except ETUMSyntaxError as e: + path = _build_item_path(tree_parent) + raise ETUMSyntaxError( + f"In: {path}\n{e._message}", + e._file or seq_filename, + ) from e item.is_folded = is_folded child = {} # case where the test item loads itself its descendants