Skip to content

Commit 8da331f

Browse files
committed
move validation to type
1 parent d141acd commit 8da331f

File tree

4 files changed

+34
-13
lines changed

4 files changed

+34
-13
lines changed

structured_tutorials/models/tests.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import re
77
from typing import Annotated, Literal
88

9-
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, field_validator, model_validator
9+
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, model_validator
1010

1111
from structured_tutorials.models.base import CommandBaseModel, CommandType, TestSpecificationMixin
1212
from structured_tutorials.models.validators import validate_regex
@@ -42,16 +42,6 @@ class TestOutputModel(BaseModel):
4242
line_count: COUNT_TYPE = Field(default=None, description="Test for the given line count.")
4343
character_count: COUNT_TYPE = Field(default=None, description="Test for the given character count.")
4444

45-
@field_validator("line_count", "character_count", mode="after")
46-
@classmethod
47-
def validate_counts(cls, value: COUNT_TYPE) -> COUNT_TYPE:
48-
if isinstance(value, tuple):
49-
min, max = value
50-
if min is not None and max is not None and min > max:
51-
raise ValueError("Minimum is bigger then the maximum.")
52-
53-
return value
54-
5545
@model_validator(mode="after")
5646
def validate_tests(self) -> Self:
5747
if not self.regex and not self.line_count and not self.character_count:

structured_tutorials/models/validators.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,23 @@
66
import re
77
from typing import Any
88

9+
from pydantic import NonNegativeInt
10+
911

1012
def validate_regex(value: Any) -> Any:
1113
"""Validate and compile a regular expression."""
1214
if isinstance(value, str):
1315
return re.compile(value.encode())
1416
return value # pragma: no cover
17+
18+
19+
def validate_count_tuple(
20+
value: tuple[NonNegativeInt | None, NonNegativeInt | None],
21+
) -> tuple[NonNegativeInt | None, NonNegativeInt | None]:
22+
"""Validate that min is larger than max in a count tuple."""
23+
count_min, count_max = value
24+
if count_min is not None and count_max is not None and count_min > count_max:
25+
raise ValueError(f"Minimum ({count_min}) is greater than maximum ({count_max}).")
26+
if count_min is None and count_max is None:
27+
raise ValueError("At least one of minimum or maximum must be specified.")
28+
return value

structured_tutorials/typing.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,24 @@
33

44
"""Module that re-exports some type hints."""
55

6+
from typing import Annotated
7+
8+
from pydantic import AfterValidator, NonNegativeInt
9+
10+
from structured_tutorials.models.validators import validate_count_tuple
11+
612
try:
713
from typing import Self
814
except ImportError: # pragma: no cover
915
# Note: only for py3.10
1016
from typing_extensions import Self
1117

18+
COUNT_TUPLE = Annotated[
19+
tuple[NonNegativeInt | None, NonNegativeInt | None], AfterValidator(validate_count_tuple)
20+
]
21+
COUNT_TYPE = NonNegativeInt | COUNT_TUPLE | None
1222

1323
__all__ = [
24+
"COUNT_TYPE",
1425
"Self",
1526
]
16-
COUNT_TYPE = int | tuple[int | None, int | None] | None

tests/test_models.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,12 @@ def test_output_model_with_empty_value() -> None:
182182
@pytest.mark.parametrize("field", ("line_count", "character_count"))
183183
def test_output_model_with_min_max_mismatch(field: str) -> None:
184184
"""Test error for an TestOutputModel where min is bigger than max."""
185-
with pytest.raises(ValueError, match=r"Minimum is bigger then the maximum\."):
185+
with pytest.raises(ValueError, match=r"Minimum \(1\) is greater than maximum \(0\)\."):
186186
TestOutputModel.model_validate({field: [1, 0]})
187+
188+
189+
@pytest.mark.parametrize("field", ("line_count", "character_count"))
190+
def test_output_model_with_min_and_max_none(field: str) -> None:
191+
"""Test error for an TestOutputModel where min and max are both None."""
192+
with pytest.raises(ValueError, match=r"At least one of minimum or maximum must be specified\."):
193+
TestOutputModel.model_validate({field: [None, None]})

0 commit comments

Comments
 (0)