fix!: remove ValidationError and FormatString.validate

The FormatString.validate method is not used anywhere. Rather than
keeping it around, and maintaining an unused method, let's delete it and
reduce the amount of code to maintain. Adding it back in, if a use-case
appears is easy enough.

Signed-off-by: Daniel Neilson <53624638+ddneilson@users.noreply.github.com>

@@ -8,7 +8,6 @@
     ModelValidationError,
     TokenError,
     UnsupportedSchema,
-    ValidationError,
 )
 from ._parse import DocumentType, decode_template, document_string_to_object, parse_model
 from ._step_dependency_graph import (
@@ -63,6 +62,5 @@
     "TaskParameterSet",
     "TokenError",
     "UnsupportedSchema",
-    "ValidationError",
     "version",
 )

@@ -6,7 +6,6 @@
     "ModelValidationError",
     "TokenError",
     "UnsupportedSchema",
-    "ValidationError",
 ]

@@ -44,12 +43,6 @@ class DecodeValidationError(_BaseMessageError):
     pass

-class ValidationError(_BaseMessageError):
-    """Error raised when a value, or values, in a template are nonvalid."""
-
-    pass
-
-
 class ModelValidationError(_BaseMessageError):
     """Error raised when a validation error is encountered while validating
     a model.

@@ -3,7 +3,7 @@
 import numbers
 from typing import Union

-from .._errors import ExpressionError, ValidationError
+from .._errors import ExpressionError
 from .._symbol_table import SymbolTable
 from ._nodes import Node
 from ._parser import Parser
@@ -40,20 +40,6 @@ def validate_symbol_refs(self, *, symbols: set[str]) -> None:
         """
         self._expresion_tree.validate_symbol_refs(symbols=symbols)

-    def validate(self, *, symtab: SymbolTable) -> None:
-        """Check whether this expression can be evaluated correctly given a symbol table.
-
-        Currently, this is simply verifying that the expression references only symbols that
-        are in the given symbol table.
-
-        Args:
-            symtab (SymbolTable): A symbol table to validate against.
-
-        Raises:
-            ValidationError: If the expression cannot be evaluated given the symbol table.
-        """
-        self._expresion_tree.validate(symtab=symtab)
-
     def evaluate(self, *, symtab: SymbolTable) -> Union[numbers.Real, str]:
         """Evaluate the expression given a SymbolTable.

@@ -68,7 +54,7 @@ def evaluate(self, *, symtab: SymbolTable) -> Union[numbers.Real, str]:
         """
         try:
             result = self._expresion_tree.evaluate(symtab=symtab)
-        except ValidationError as exc:
+        except ValueError as exc:
             raise ExpressionError(f"Expression failed validation: {str(exc)}")

         if isinstance(result, (numbers.Real, str)):

@@ -4,7 +4,7 @@
 from numbers import Real
 from typing import TYPE_CHECKING, Optional, Union

-from .._errors import ExpressionError, TokenError, ValidationError
+from .._errors import ExpressionError, TokenError
 from .._symbol_table import SymbolTable
 from ._dyn_constrained_str import DynamicConstrainedStr
 from ._expression import InterpolationExpression
@@ -128,45 +128,6 @@ def resolve(self, *, symtab: SymbolTable) -> str:

         return "".join(resolved_list)

-    def validate(self, *, symtab: SymbolTable) -> list[FormatStringError]:
-        """
-        Uses a given symbol table to validate an interpolated string.
-        Verifies that each interpolation expression in the original string
-        can be replaced by a value from the symbol table.
-        Does not raise any exception if the interpolated string is valid.
-
-        Parameters
-        ----------
-        symtab: SymbolTable
-            A symbol table with values that are used to validate interpolation
-            expressions in the interpolated string.
-            For example, tle format string '{{Some.data}}' is valid if the table
-            contains the value for 'Some.data'.
-
-        Returns
-        -------
-        validation_errors: list[FormatStringError]
-            A list of errors for every interpolation expression in the
-            format string that can not be resolved with a given symbol table.
-        """
-        validation_errors: list[FormatStringError] = []
-
-        for expression_info in self.expressions:
-            assert expression_info.expression is not None
-            try:
-                expression_info.expression.validate(symtab=symtab)
-            except ValidationError as exc:
-                validation_errors.append(
-                    FormatStringError(
-                        string=self.original_value,
-                        start=expression_info.start_pos,
-                        end=expression_info.end_pos,
-                        details=str(exc),
-                    )
-                )
-
-        return validation_errors
-
     def _preprocess(self) -> list[Union[str, ExpressionInfo]]:
         """
         Scans through the original string to find all interpolation expressions inside of {{ }}.

@@ -7,7 +7,6 @@
 from typing import Any

 from .._symbol_table import SymbolTable
-from .._errors import ValidationError

 class Node(ABC):
@@ -32,34 +31,13 @@ def validate_symbol_refs(self, *, symbols: set[str]) -> None:  # pragma: no cove
         """
         pass

-    @abstractmethod
-    def validate(self, *, symtab: SymbolTable) -> None:  # pragma: no cover
-        """Verifies that the expression rooted at this node is valid
-        given the definitions of symbols in a symbol table.
-
-        For example, an expression is not valid if it references
-        a symbol that does not exist in the symbol table.
-
-        Raises:
-            ValidationError: If the expression is not valid. The given error contains
-               context and information on the specifics of the error.
-
-        Args:
-            symtab (SymbolTable): Symbol definitions to validate against.
-
-        Note: The symtab does need to have placeholder values for each symbol
-        that are of the appropriate type. For example, a placeholder for a
-        number-type symbol might be the integer value 0.
-        """
-        pass
-
     @abstractmethod
     def evaluate(self, *, symtab: SymbolTable) -> Any:  # pragma: no cover
         """Evaluate the expression rooted at this node given definitions
         of symbols in a symbol table.

         Raises:
-            ValidationError: If the expression is not valid. The given error contains
+            ValueError: If the expression is not valid. The given error contains
                context and information on the specifics of the error.

         Args:
@@ -90,12 +68,9 @@ def validate_symbol_refs(self, *, symbols: set[str]) -> None:
                 f"{self.name} is referenced by an expression, but is out of scope or has no value"
             )

-    def validate(self, *, symtab: SymbolTable) -> None:
-        if self.name not in symtab:
-            raise ValidationError(f"{self.name} has no value")
-
     def evaluate(self, *, symtab: SymbolTable) -> Any:
-        self.validate(symtab=symtab)
+        if self.name not in symtab:
+            raise ValueError(f"{self.name} has no value")
         return symtab[self.name]

     def __repr__(self):

@@ -4,7 +4,7 @@

 import pytest

-from openjd.model import ExpressionError, SymbolTable, TokenError, ValidationError
+from openjd.model import ExpressionError, SymbolTable, TokenError
 from openjd.model._format_strings._expression import InterpolationExpression
 from openjd.model._format_strings._parser import Parser

@@ -34,34 +34,6 @@ def test_init_reraises_tokenizer_error(self):
         with pytest.raises(TokenError):
             InterpolationExpression(expr)

-    def test_validate_success(self):
-        # GIVEN
-        symtab = SymbolTable()
-        symtab["Test.Name"] = "value"
-
-        # WHEN
-        expr = InterpolationExpression("Test.Name")
-
-        # THEN
-        try:
-            expr.validate(symtab=symtab)
-        except ValidationError:
-            pytest.fail("Incorrectly identified expression as nonvalid.")
-
-    def test_validate_fails(self):
-        # GIVEN
-        symtab = SymbolTable()
-        symtab["Test.Name"] = "value"
-
-        # WHEN
-        expr = InterpolationExpression("Test.Fail")
-
-        # THEN
-        with pytest.raises(ValidationError) as exc:
-            expr.validate(symtab=symtab)
-
-        assert "Test.Fail" in str(exc), "Name should be in validation error"
-
     def test_evaluate_success(self):
         # GIVEN
         symtab = SymbolTable()

@@ -1,6 +1,7 @@
 # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

 import pytest
+from typing import Union

 from openjd.model import SymbolTable
 from openjd.model._format_strings import FormatString, FormatStringError
@@ -52,131 +53,26 @@ def test_nonvalid_strings(input):

 class TestFormatStringResolve:
-    def test_empty_string(self):
-        # GIVEN
-        input = ""
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == ""
-
-    def test_no_expressions(self):
-        # GIVEN
-        input = "input"
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == "input"
-
-    def test_single_expr_no_space(self):
-        # GIVEN
-        input = "{{Test.val}}"
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == "4"
-
-    def test_single_expr_with_space(self):
-        # GIVEN
-        input = " {{Test.val}} "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == " 4 "
-
-    def test_with_spaces_on_both_sides(self):
-        # GIVEN
-        input = " {{ Test.val }} "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == " 4 "
-
-    def test_multiple_expressions(self):
-        # GIVEN
-        input = " {{ Test.val }}-{{    Test.end}}  "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4
-        symtab["Test.end"] = 10
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == " 4-10  "
-
-    def test_with_floating_point(self):
-        # GIVEN
-        input = " {{ Test.val }}-{{    Test.end}}  "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4.098
-        symtab["Test.end"] = 10
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == " 4.098-10  "
-
-    def test_with_string(self):
-        # GIVEN
-        input = " {{ Test.val }}-{{    Test.end}}  "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4.098
-        symtab["Test.end"] = "hello1"
-
-        # THEN
-        assert format_string.resolve(symtab=symtab) == " 4.098-hello1  "
-
-    def test_without_entry_in_table(self):
-        # GIVEN
-        input = " {{ Test.val }}-{{    Test.end}}  "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        symtab["Test.val"] = 4.098
-
-        # THEN
-        with pytest.raises(FormatStringError, match="Failed to parse interpolation expression"):
-            format_string.resolve(symtab=symtab)
-
-
-class TestFormatStringValidate:
     @pytest.mark.parametrize("input", ["", "input"])
-    def test_correct_validation_with_empty_table(self, input):
+    def test_with_empty_table(self, input: str) -> None:
         # GIVEN
         symtab = SymbolTable()

         # WHEN
         format_string = FormatString(input)

         # THEN
-        assert format_string.validate(symtab=symtab) == []
+        assert format_string.resolve(symtab=symtab) == input

-    @pytest.mark.parametrize("input", ["{{Test.val}}", " {{Test.val}} ", " {{ Test.val }} "])
-    def test_correct_validation_with_table(self, input):
+    @pytest.mark.parametrize(
+        "input, expected",
+        [
+            pytest.param("{{Test.val}}", "4"),
+            pytest.param(" {{Test.val}} ", " 4 "),
+            pytest.param(" {{ Test.val }} ", " 4 "),
+        ],
+    )
+    def test_with_value(self, input: str, expected: str) -> None:
         # GIVEN
         symtab = SymbolTable()

@@ -185,17 +81,21 @@ def test_correct_validation_with_table(self, input):
         symtab["Test.val"] = 4

         # THEN
-        assert format_string.validate(symtab=symtab) == []
+        assert format_string.resolve(symtab=symtab) == expected

     @pytest.mark.parametrize(
-        "input,val,end",
+        "input,expected,val,end",
         [
-            (" {{ Test.val }}-{{    Test.end}}  ", 4, 10),
-            (" {{ Test.val }}-{{    Test.end}}  ", 4.098, 10),
-            (" {{ Test.val }}-{{    Test.end}}  ", 4.098, "hello1"),
+            ("{{ Test.val }}-{{    Test.end}}", "4-10", 4, 10),
+            (" {{ Test.val }}-{{    Test.end}}  ", " 4-10  ", 4, 10),
+            (" {{ Test.val }} - {{    Test.end}}  ", " 4 - 10  ", 4, 10),
+            (" {{ Test.val }}-{{    Test.end}}  ", " 4.098-10  ", 4.098, 10),
+            (" {{ Test.val }}-{{    Test.end}}  ", " 4.098-hello1  ", 4.098, "hello1"),
         ],
     )
-    def test_multiple_expressions(self, input, val, end):
+    def test_multiple_expressions(
+        self, input: str, expected: str, val: Union[float, int], end: Union[float, int]
+    ):
         # GIVEN
         symtab = SymbolTable()

@@ -205,29 +105,17 @@ def test_multiple_expressions(self, input, val, end):
         symtab["Test.end"] = end

         # THEN
-        assert format_string.validate(symtab=symtab) == []
+        assert format_string.resolve(symtab=symtab) == expected

-    def test_without_one_entry_in_table(self):
+    def test_without_entry_in_table(self):
         # GIVEN
         input = " {{ Test.val }}-{{    Test.end}}  "
         symtab = SymbolTable()

         # WHEN
         format_string = FormatString(input)
         symtab["Test.val"] = 4.098
-        errors = format_string.validate(symtab=symtab)
-
-        # THEN
-        assert len(errors) == 1
-
-    def test_without_multiple_entries_in_table(self):
-        # GIVEN
-        input = " {{ Test.val }}-{{    Test.end}}  "
-        symtab = SymbolTable()
-
-        # WHEN
-        format_string = FormatString(input)
-        errors = format_string.validate(symtab=symtab)

         # THEN
-        assert len(errors) == 2
+        with pytest.raises(FormatStringError, match="Failed to parse interpolation expression"):
+            format_string.resolve(symtab=symtab)

@@ -2,39 +2,11 @@

 import pytest

-from openjd.model import SymbolTable, ValidationError
+from openjd.model import SymbolTable
 from openjd.model._format_strings._nodes import FullNameNode

 class TestFullNameNode:
-    def test_validate_success(self):
-        # GIVEN
-        symtab = SymbolTable()
-        symtab["Test.Name"] = "value"
-
-        # WHEN
-        node = FullNameNode("Test.Name")
-
-        # THEN
-        try:
-            node.validate(symtab=symtab)
-        except ValidationError:
-            pytest.fail("Incorrectly identified expression as nonvalid.")
-
-    def test_validate_fails(self):
-        # GIVEN
-        symtab = SymbolTable()
-        symtab["Test.Name"] = "value"
-
-        # WHEN
-        node = FullNameNode("Test.Fail")
-
-        # THEN
-        with pytest.raises(ValidationError) as exc:
-            node.validate(symtab=symtab)
-
-        assert "Test.Fail" in str(exc), "Name should be in validation error"
-
     def test_evaluate_success(self):
         # GIVEN
         symtab = SymbolTable()
@@ -56,7 +28,7 @@ def test_evaluate_fails(self):
         node = FullNameNode("Test.Fail")

         # THEN
-        with pytest.raises(ValidationError) as exc:
+        with pytest.raises(ValueError) as exc:
             node.evaluate(symtab=symtab)

         assert "Test.Fail" in str(exc), "Name should be in validation error"

@@ -5,7 +5,6 @@
     ExpressionError,
     TokenError,
     UnsupportedSchema,
-    ValidationError,
 )

@@ -27,15 +26,6 @@ def test_msg(self):
         assert str(error) == "Test message"

-class TestValidationError:
-    def test_msg(self):
-        # GIVEN
-        error = ValidationError("Test message")
-
-        # THEN
-        assert str(error) == "Test message"
-
-
 class TestExpressionError:
     def test_msg(self):
         # GIVEN


fix!: remove ValidationError and FormatString.validate

### What was the problem/requirement? (What/Why)

The `FormatString.validate` method is not used anywhere. Rather than keeping it around, and maintaining an unused method, let's delete it and reduce the amount of code to maintain. Adding it back in, if a use-case appears is easy enough.

We also do not need the `ValidationError` exception type.

### What was the solution? (How)

Delete the method and the error type.

### What is the impact of this change?

Less to maintain.

### How was this change tested?

I ran unit tests, and also verified that none of the other Open Job Description code uses the deleted code.

### Was this change documented?

N/A

### Is this a breaking change?

Yes

----

*By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.*

openjd-model-for-python

Provides a Python implementation of the data model for Open Job Description's template schemas.