]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
python: modeling: added error classes
authorAleš Mrázek <ales.mrazek@nic.cz>
Mon, 16 Feb 2026 18:11:28 +0000 (19:11 +0100)
committerAleš Mrázek <ales.mrazek@nic.cz>
Mon, 20 Apr 2026 22:25:58 +0000 (00:25 +0200)
python/knot_resolver/utils/modeling/errors.py [new file with mode: 0644]
tests/python/knot_resolver/utils/modeling/test_errors.py [new file with mode: 0644]

diff --git a/python/knot_resolver/utils/modeling/errors.py b/python/knot_resolver/utils/modeling/errors.py
new file mode 100644 (file)
index 0000000..133ec00
--- /dev/null
@@ -0,0 +1,115 @@
+from __future__ import annotations
+
+from knot_resolver.errors import BaseKresError
+
+
+class DataModelingError(BaseKresError):
+    """Base exception class for all data modeling errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        super().__init__()
+        self._msg = f"[{error_pointer}] {msg}" if error_pointer else msg
+        self._error_pointer = error_pointer
+
+    def __str__(self) -> str:
+        return self._msg
+
+
+class DataDescriptionError(DataModelingError):
+    """Exception class for data description errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        msg = f"description error: {msg}"
+        super().__init__(msg, error_pointer)
+
+
+class DataAnnotationError(DataModelingError):
+    """Exception class for data annotation errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        msg = f"annotation error: {msg}"
+        super().__init__(msg, error_pointer)
+
+
+class DataParsingError(DataModelingError):
+    """Exception class for data parsing errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        msg = f"parsing error: {msg}"
+        super().__init__(msg, error_pointer)
+
+
+class DataTypeError(DataModelingError):
+    """Exception class for data type errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        msg = f"type error: {msg}"
+        super().__init__(msg, error_pointer)
+
+
+class DataValueError(DataModelingError):
+    """Exception class for data value errors."""
+
+    def __init__(self, msg: str, error_pointer: str = "") -> None:
+        msg = f"value error: {msg}"
+        super().__init__(msg, error_pointer)
+
+
+class DataValidationError(DataModelingError):
+    """Exception class for data validation errors.
+
+    This exception is used as parent for other data modeling errors.
+    """
+
+    def __init__(self, msg: str, error_pointer: str, child_errors: list[DataModelingError] | None = None) -> None:
+        super().__init__(msg, error_pointer)
+
+        if child_errors is None:
+            child_errors = []
+        self._child_errors = child_errors
+
+    def recursive_msg(self, indentation: int = 0) -> str:
+        parts: list[str] = []
+
+        if indentation == 0:
+            indentation += 1
+            parts.append("Data validation error detected:")
+
+        indent = "    " * indentation
+        parts.append(f"{indent}{self._msg}")
+
+        if self._child_errors:
+            for error in self._child_errors:
+                if isinstance(error, DataValidationError):
+                    parts.append(error.recursive_msg(indentation + 1))
+                else:
+                    parts.append(indent + f"    {error}")
+        return "\n".join(parts)
+
+    def __str__(self) -> str:
+        return self.recursive_msg()
+
+
+class AggrDataValidationError(DataValidationError):
+    """Exception class for aggregation of data validation errors.
+
+    This exception is used to aggregate other data modeling errors.
+    """
+
+    def __init__(self, error_pointer: str, child_errors: list[DataModelingError]) -> None:
+        super().__init__("error due to lower level error", error_pointer, child_errors)
+
+    def recursive_msg(self, indentation: int = 0) -> str:
+        inc = 0
+        parts: list[str] = []
+
+        if indentation == 0:
+            inc = 1
+            parts.append("Data validation errors detected:")
+
+        for error in self._child_errors:
+            if isinstance(error, DataValidationError):
+                parts.append(error.recursive_msg(indentation + inc))
+            else:
+                parts.append(f"    {error}")
+        return "\n".join(parts)
diff --git a/tests/python/knot_resolver/utils/modeling/test_errors.py b/tests/python/knot_resolver/utils/modeling/test_errors.py
new file mode 100644 (file)
index 0000000..0486ff7
--- /dev/null
@@ -0,0 +1,46 @@
+import pytest
+
+from knot_resolver.utils.modeling.errors import (
+    AggrDataValidationError,
+    DataAnnotationError,
+    DataDescriptionError,
+    DataModelingError,
+    DataTypeError,
+    DataValidationError,
+    DataValueError,
+)
+
+errors = [
+    DataModelingError("this is data modeling error message", "/error"),
+    DataAnnotationError("this is annotation error message", "/annotation"),
+    DataDescriptionError("this is description error message", "/description"),
+    DataTypeError("this is type error message", "/type"),
+    DataValueError("this is value error message", "/value"),
+]
+
+
+def test_data_validation_error() -> None:
+    error_msg = """Data validation error detected:
+    [/validation] this is validation error message
+        [/error] this is data modeling error message
+        [/annotation] annotation error: this is annotation error message
+        [/description] description error: this is description error message
+        [/type] type error: this is type error message
+        [/value] value error: this is value error message"""
+
+    with pytest.raises(DataValidationError) as error:
+        raise DataValidationError("this is validation error message", "/validation", errors)
+    assert str(error.value) == error_msg
+
+
+def test_aggregate_data_validation_error() -> None:
+    error_msg = """Data validation errors detected:
+    [/error] this is data modeling error message
+    [/annotation] annotation error: this is annotation error message
+    [/description] description error: this is description error message
+    [/type] type error: this is type error message
+    [/value] value error: this is value error message"""
+
+    with pytest.raises(AggrDataValidationError) as error:
+        raise AggrDataValidationError("/", errors)
+    assert str(error.value) == error_msg