3 · Validation
Three entry points. Pick the one matching the data shape, not the framework.
Why
Different stages of an application care about different shapes. Inside a service, you have objects. At an HTTP boundary, you have raw dicts. In a data pipeline, you have streams. Forcing all three to go through one entry point would mean instantiating objects you do not need or losing laziness.
How
| Function | Input | When to use |
|---|---|---|
validate(instance) |
A @monk class instance |
Validate DTOs you have already built. Returns the (now-unlocked) instance for inline use. |
validate_value(value, *constraints) |
A single value + one or more constraints | Ad-hoc validation outside a @monk class — controllers, scripts, one-shot checks. |
validate_dict(data, schema) |
A raw dict + TypedDict / @monk class |
High-throughput APIs that never instantiate objects. Supports partial=True (PATCH) and drop_extra_keys=True (sanitization). |
validate_stream(iterable, constraints) |
A generator / iterator | Lazy validation of large streams. The async sibling is validate_async_stream. |
from monk import validate, validate_value, validate_dict, validate_stream
# Object: deferred until called explicitly
user = User(email="kai@example.com", age=30)
validate(user)
# Single value: skip the dataclass
validate_value(uploaded_body, MagicBytes(allowed=("image/png",)), FileSize(max_size=5_000_000))
# Dict: skip instantiation entirely
validate_dict({"email": "kai@example.com", "age": 30}, User)
# Stream: validate items as they are pulled
for item in validate_stream(big_generator, [Interval(gt=0)]):
process(item)
Gotchas
validate(instance)is a no-op for non-monk classes. It raisesTypeErrorto surface accidental misuse rather than silently passing.validate_valueaggregates failures. Every constraint runs, and all per-constraint errors land in a singleValidationError. Matchesvalidate()semantics — not fail-fast.validate_dictdoes not allocate the object. Side effects in__post_init__will not fire — that is by design. Usevalidate(MyClass(**data))if you need them.- Streams are exhausted.
validate_streamyields each validated item; consume the generator.
For execution-time flags (partial, drop-extra-keys, recursive nesting) see Advanced → Execution Models.
Next: Pillar 4 — Errors