Skip to content

Commit

Permalink
Added non-default validations
Browse files Browse the repository at this point in the history
  • Loading branch information
Naapperas committed Jun 16, 2024
1 parent df33ec7 commit f6d3e60
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- Added `ValidationContext` class to keep track of current validation path and errors up until a certain point.
- Implemented non-default validation
- Added dimension tests for `ZonString`

### Changed
- Moved everything into a single file to combat circular reference issues
Expand Down
25 changes: 25 additions & 0 deletions tests/str_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@ def test_str_safe_validate(validator):
assert validator.safe_validate([1])[0] is False
assert validator.safe_validate({"a": 1})[0] is False

def test_str_length_equal(validator):
_validator = validator.length(2)

assert _validator.validate("12")

with pytest.raises(zon.error.ZonError):
_validator.validate("1")

def test_str_length_less_than(validator):
_validator = validator.max(3)

assert _validator.validate("1")
assert _validator.validate("12")

with pytest.raises(zon.error.ZonError):
_validator.validate("123")

def test_str_length_greater_than(validator):
_validator = validator.min(1)

assert _validator.validate("123")
assert _validator.validate("12")

with pytest.raises(zon.error.ZonError):
_validator.validate("1")

"""
def test_email(validator):
Expand Down
43 changes: 34 additions & 9 deletions zon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,17 @@ def dirty(self):
return self._error is not None and len(self._error.issues) >= 0

T = TypeVar("T")
ValidationRule = Callable[[T, ValidationContext], bool]

class ValidationRule:
def __init__(self, fn: Callable[[T, ValidationContext], bool]):
def __init__(self, name: str, fn: Callable[[T], bool]):
self.fn = fn
self.name = name

def check(self, data: Any, ctx: ValidationContext):
return self.fn(data, ctx)
valid = self.fn(data)

if not valid:
ctx.add_issue(ZonIssue(value=data, message=f"Validation failed for type {self.name}", path=[]))

class Zon(ABC):
"""
Expand Down Expand Up @@ -189,33 +192,53 @@ class ZonContainer(Zon, HasMax, HasMin):
Contains container specific validator rules.
"""

def max(self, max_value: int | float):
def max(self, max_value: int | float) -> Self:
"""Validates that this container as at most `max_value` elements (exclusive).
Args:
max_value (int | float): the maximum number of elements that this container can have
"""

# TODO: add check
_clone = self._clone()

_clone.validators.append(ValidationRule(
"length",
lambda data: len(data) < max_value,
))

def min(self, min_value: int | float):
return _clone

def min(self, min_value: int | float) -> Self:
"""Validates that this container as at least `max_value` elements (exclusive).
Args:
min_value (int | float): the minimum number of elements that this container can have
"""

# TODO: add check
_clone = self._clone()

_clone.validators.append(ValidationRule(
"length_min",
lambda data: len(data) > min_value,
))

def length(self, length: int):
return _clone

def length(self, length: int) -> Self:
"""Validates that this container as exactly `length` elements.
Args:
length (int): the exact number of elements that this container can have
"""

# TODO: add check
_clone = self._clone()

_clone.validators.append(ValidationRule(
"length_equal",
lambda data: len(data) == length,
))

return _clone

def string(*, fast_termination=False) -> ZonString:
"""Returns a validator for string data.
Expand All @@ -238,3 +261,5 @@ class ZonString(ZonContainer):
def _default_validate(self, data: T, ctx: ValidationContext):
if not isinstance(data, str):
ctx.add_issue(ZonIssue(value=data, message="Not a string", path=[]))


5 changes: 3 additions & 2 deletions zon/traits.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Self


class HasMax:
Expand All @@ -13,7 +14,7 @@ class HasMax:
"""

@abstractmethod
def max(self, max_value: int | float):
def max(self, max_value: int | float) -> Self:
"""
Defines that a given attribute of the value being validated must be upper-bound by the given parameter.
Expand All @@ -30,7 +31,7 @@ class HasMin:
"""

@abstractmethod
def min(self, min_value: int | float):
def min(self, min_value: int | float) -> Self:
"""
Defines that a given attribute of the value being validated must be lower-bound by the given parameter.
Expand Down

0 comments on commit f6d3e60

Please sign in to comment.