Skip to content

Commit

Permalink
Merge pull request #4 from Naapperas/feature/validator-refinement
Browse files Browse the repository at this point in the history
Added refine method
  • Loading branch information
Naapperas authored Dec 20, 2023
2 parents 7dddfe9 + a9fc541 commit a3c996f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.vscode
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

> This changelog was generated some commits after the [v1.0.0 tag](https://github.com/Naapperas/zon/releases/tag/v1.0.0), so the changelog will have some inconsistencies until the next release.
Expand All @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added the `zon.traits.collection` file which contains the `ZonCollection` class: this is the base class for all collection types.
- Added testing for `ZonCollection` and added more tests for `ZonString`.
- Scripts that automate the building and publishing of the package to PyPI.
- Added `refine` method to the base `Zon` class.

### Changed
- `ZonString` now inherits from `ZonCollection` instead of `Zon`.
Expand Down
16 changes: 16 additions & 0 deletions tests/base_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest

import zon

@pytest.fixture
def validator():
return zon.anything()

def test_refine(validator):

assert validator.validate(1)

refined_validator = validator.refine(lambda x: x == 1)

assert refined_validator.validate(1)
assert not refined_validator.validate(2)
40 changes: 39 additions & 1 deletion zon/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
ValidationRule = Callable[[T], bool]


class AggregateValidator:
"""A validator that aggregates multiple validation calls"""


class Zon(ABC):
"""Base class for all Zons.
A Zon is the basic unit of validation in Zon.
Expand Down Expand Up @@ -97,6 +101,40 @@ def and_also(self, zon: "Zon") -> "ZonAnd":
def __and__(self, zon: "Zon") -> "ZonAnd":
return self.and_also(zon)

def refine(self, refinement: Callable[[T], bool]) -> "Self":
"""Creates a new Zon that validates the data with the supplied refinement.
A refinement is a function that takes a piece of data and returns True if the data is valid or throws otherwise.
Args:
refinement (Callable[[T], bool]): the refinement to be applied.
Returns:
ZonRefined: a new validator that validates the data with the supplied refinement.
"""
_clone = self._clone()

def _refinement_validate(data):
try:
return refinement(data)
except ValidationError as e:
_clone._add_error(e)
return False

if "_refined_" not in _clone.validators:
_clone.validators["_refined_"] = _refinement_validate
else:
current_refinement = _clone.validators["_refined_"]

def _refined_validator(data):
return current_refinement(data) and _refinement_validate(
data
)

_clone.validators["_refined_"] = _refined_validator

return _clone


def optional(zon: Zon) -> "ZonOptional":
"""Marks this validation chain as optional, making it so the data supplied need not be defined.
Expand Down Expand Up @@ -129,7 +167,7 @@ def _default_validate(self, data):
class ZonAnd(Zon):
"""A Zon that validates that the data is valid for both this Zon and the supplied Zon."""

def __init__(self, zon1, zon2):
def __init__(self, zon1: Zon, zon2: Zon):
super().__init__()
self.zon1 = zon1
self.zon2 = zon2
Expand Down

0 comments on commit a3c996f

Please sign in to comment.