Skip to content
Cascading Labs QScrape VoidCrawl Yosoi

Validators

Yosoi offers two layers of field-level validation: built-in type coercions that handle the common cases, and a Validators inner class for custom transforms.

Built-in Coercions

Field types like ys.Price(), ys.Title(), and ys.Author() coerce raw strings automatically — no @field_validator needed.

import yosoi as ys
class Product(ys.Contract):
title: str = ys.Title()
price: float = ys.Price(hint='Book price, always includes £ symbol')
rating: str = ys.Rating(hint="Star rating written as a word e.g. 'Three'")
raw = {'title': ' A Light in the Attic ', 'price': '£12.99', 'rating': ' Three '}
result = Product.model_validate(raw)
# result.title == 'A Light in the Attic' (whitespace stripped)
# result.price == 12.99 (float, £ stripped)
# result.rating == 'Three' (whitespace stripped)

Validators Inner Class

For custom per-field transforms, define static methods inside a Validators class. They run before Pydantic’s own validation. No decorator ceremony required.

class BookStore(ys.Contract):
title: str = ys.Title()
price: float = ys.Price(hint='Book price including currency symbol')
category: str = ys.Field(hint='Genre or category label')
class Validators:
@staticmethod
def title(v: str) -> str:
"""Truncate very long titles to 60 characters."""
return v[:60].rstrip() + ('...' if len(v) > 60 else '')
@staticmethod
def category(v: str) -> str:
"""Normalise category to title case."""
return v.strip().title()
raw = {
'title': 'The Very Long Title That Goes On and On and Eventually Exceeds Sixty Characters',
'price': '$1,234.56',
'category': ' science fiction ',
}
result = BookStore.model_validate(raw)
# result.title == 'The Very Long Title That Goes On and On and Eventually...'
# result.price == 1234.56 ($ and , stripped by ys.Price)
# result.category == 'Science Fiction'

Each method name must match the field name it transforms. Methods that don’t match any field are silently ignored.

FAQs

Can I raise an error inside a Validators method?

Yes. Raise a standard ValueError and Pydantic will wrap it into a ValidationError as usual.

Do validators run on list[T] fields element-by-element?

No. The validator receives the full list as its argument. If you need per-element processing, iterate inside the method.

Can I use async validators?

No. Validators methods must be synchronous static methods. For async post-processing, handle it after model_validate() returns.

What is the execution order?

Validators methods run first, then built-in type coercions, then Pydantic’s own validation. This means your custom transforms see the raw extracted string before any coercion has been applied.

References

Pydantic. Pydantic Services Inc. Data validation library for Python. https://docs.pydantic.dev/