Validation
A fluent, expressive validation engine. Sanitize inputs, enforce complex rules, and ensure data integrity with a type-safe Python API.
Rules Engine
Chain multiple constraints like
required,
email, and
min:8 using a
simple syntax.
Sanitization
Built-in helpers to clean strings, escape HTML, and normalize data formats (like Dates and Booleans) automatically.
Type Safe
Includes a Rule class helper to provide IntelliSense and
prevent string typos in your validation logic.
Direct Validation
Use static methods for quick, single-value checks. These methods return
the sanitized value on success, or None on failure.
from casp.validate import Validate
email = Validate.email(" User@Example.com ")
# Result: "User@Example.com" (Trimmed & Validated)
is_valid = Validate.cuid2("tz4a98xxat96iws9zmbrgj3a")
# Result: "tz4a98xxat96iws9zmbrgj3a"
invalid = Validate.url("not-a-url")
# Result: None
The Rules Engine
For complex logic, use with_rules. You can pass a
pipe-separated string (Laravel style) or use the Rule class
for better DX.
from casp.validate import Validate, Rule
def register_user(data):
username = data.get("username")
# 1. String Syntax (Quick & Dirty)
check = Validate.with_rules(username, "required|alpha_num|min:3|max:20")
# 2. Class Syntax (Recommended for Type Safety)
check_safe = Validate.with_rules(username, [
Rule.REQUIRED,
Rule.min(3),
Rule.max(20),
Rule.not_in_list(["admin", "root"])
])
if check_safe is not True:
return "error": check_safe # Returns specific error message
return "success": True
Field Errors With Validator
with_rules checks one value and returns its first failure. To validate a whole
form and return every field's message at once, use Validator.
from casp.rpc import rpc
from casp.validate import Rule, Validator
@rpc()
async def register(email: str = "", password: str = "", password_confirmation: str = ""):
v = Validator({"email": email, "password": password})
v.rules("email", [Rule.REQUIRED, Rule.EMAIL])
v.rules(
"password",
[Rule.REQUIRED, Rule.min(10), Rule.confirmed()],
confirmation=password_confirmation,
)
if await email_taken(email):
v.add("email", "That email is already registered.")
v.finish() # raises ValidationError when anything failed
return await create_account(**v.validated())
- Each field stops at its first failing rule; failures are collected across fields.
-
Messages read "The email field is required." Pass
label=to rename a field. -
A field without
requiredis skipped when its value is empty. -
check(field, passes, message)adds a custom rule;add(field, message)records an async check such as uniqueness — keep the database constraint as the final authority. -
errors,fails(),passes(), andvalidated()inspect the result without raising.
| Caller | finish() raises ValidationError (422) |
|---|---|
| pp.rpc | error (the first message of each field) plus an errors map of field to messages |
| route.py | Problem Details with an errors member |
| page | The nearest error.py, with error.details["errors"] |
In the browser the rejected pp.rpc promise is an RpcError whose
errors, status, and requestId carry that response:
<form onsubmit="{submit(event)}">
<input name="email" />
<p hidden="{!fieldErrors.email}">{fieldErrors.email?.[0]}</p>
<button>Create account</button>
<script>
const [fieldErrors, setFieldErrors] = pp.state({});
async function submit(event) {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget).entries());
try {
await pp.rpc("register", data);
setFieldErrors({});
} catch (error) {
setFieldErrors(error.errors ?? {});
}
}
</script>
</form>
Never put secrets, submitted passwords, or database diagnostics in validation messages.
Available Rules
| Rule Name | Example Usage | Description |
|---|---|---|
| required | Rule.REQUIRED | Checks if value is not null, empty string, or empty list. |
| Rule.EMAIL |
Validates email format (supports email-validator if
installed).
|
|
| min / max | Rule.min(5) | Enforces string length constraints. |
| confirmed | Rule.confirmed() |
Checks if value matches a confirmation_value passed
to with_rules.
|
| in / notIn | Rule.in_list(["A", "B"]) | Ensures the value exists (or does not exist) in a provided list. |
| regex | Rule.regex(r"^[a-z]+$") | Validates against a custom Regular Expression. |