Zodic
A TypeScript Zod-inspired validation library for Python with excellent type safety and developer experience.
Features
- Type-safe validation with excellent IDE support and autocompletion
- Intuitive chainable API for building complex schemas
- Lightning-fast performance - 2M+ operations/second
- Comprehensive error reporting with detailed nested paths
- Zero dependencies - lightweight and fast imports
- Rich validation types - strings, numbers, dates, enums, literals, and more
- Advanced string validation - email, URL, regex patterns
- Date/time parsing - ISO formats and common date strings
- Union types - flexible validation with
|operator - Extensible architecture for custom validators
- Framework agnostic - works with FastAPI, Django, Flask, etc.
Installation
pip install zodic
Requires Python 3.9+
Quick Start
import zodic as z
# Basic validation
name_schema = z.string().min(1).max(100)
name = name_schema.parse("Alice") # Returns "Alice"
# Object validation
user_schema = z.object({
"name": z.string().min(1),
"age": z.number().int().min(0).max(120),
"email": z.string().email().optional(),
"is_active": z.boolean().default(True),
"role": z.enum(["admin", "user", "guest"])
})
# Parse and validate data
user_data = {
"name": "Alice Johnson",
"age": 30,
"email": "alice@example.com"
}
user = user_schema.parse(user_data)
# Returns: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": True}
Documentation
Basic Types
import zodic as z
# Primitives
z.string() # str
z.number() # int | float
z.boolean() # bool
z.none() # None
# String validation
z.string().min(5) # Minimum length
z.string().max(100) # Maximum length
z.string().length(10) # Exact length
# Number validation
z.number().int() # Must be integer
z.number().positive() # > 0
z.number().min(0).max(100) # Range validation
# Collections
z.array(z.string()) # List[str]
z.object({"name": z.string()}) # Dict with typed fields
Advanced Features
# Optional and nullable
z.string().optional() # str | None (can be missing)
z.string().nullable() # str | None (can be null)
z.string().default("hello") # Default value if missing
# Transformations
z.string().transform(str.upper) # Transform after validation
z.number().transform(lambda x: x * 2) # Custom transformations
# Custom validation
z.string().refine(
lambda x: x.startswith("prefix_"),
"Must start with 'prefix_'"
)
# Union types
z.union([z.string(), z.number()]) # str | int | float
z.string() | z.number() # Same as above (v0.2.0+)
New in v0.2.0
# Literal and enum validation
z.literal("admin") # Exact value match
z.enum(["red", "green", "blue"]) # Multiple choice
# Enhanced string validation
z.string().email() # Email format validation
z.string().url() # URL format validation
z.string().regex(r"^[A-Z]{2,3}$") # Custom regex patterns
# Date and datetime validation
z.date() # Parse dates from strings or objects
z.datetime() # Parse datetimes with timezone support
z.date().min(date(2024, 1, 1)) # Date range validation
# Examples
email_schema = z.string().email()
email_schema.parse("user@example.com") # Valid
theme_schema = z.enum(["light", "dark"])
theme_schema.parse("light") # Valid
date_schema = z.date()
date_schema.parse("2024-12-19") # Returns date(2024, 12, 19)
date_schema.parse(datetime.now()) # Converts to date
Error Handling
# Parse (throws ZodError on failure)
try:
result = schema.parse(data)
except z.ZodError as e:
print(f"Validation failed: {e}")
print(f"Issues: {e.issues}")
# Safe parse (returns result object)
result = schema.safe_parse(data)
if result["success"]:
print(f"Valid data: {result['data']}")
else:
print(f"Validation errors: {result['error']}")
# Error formatting
try:
schema.parse(invalid_data)
except z.ZodError as e:
# Get flattened errors
errors = e.flatten()
# {"field.path": ["error message"]}
# Get formatted errors
formatted = e.format()
# [{"code": "invalid_type", "message": "...", "path": [...]}]
Real-World Example
import zodic as z
from datetime import date
# API request validation
create_user_schema = z.object({
"personal_info": z.object({
"first_name": z.string().min(1).max(50),
"last_name": z.string().min(1).max(50),
"email": z.string().email(),
"birth_date": z.date().max(date.today()),
"phone": z.string().regex(r"^\+?1?\d{9,15}$").optional()
}),
"account": z.object({
"username": z.string().min(3).max(30).regex(r"^[a-zA-Z0-9_]+$"),
"password": z.string().min(8).regex(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)"),
"role": z.enum(["admin", "moderator", "user"]).default("user"),
"permissions": z.array(z.string()).default([])
}),
"preferences": z.object({
"theme": z.enum(["light", "dark", "auto"]).default("auto"),
"language": z.string().regex(r"^[a-z]{2}$").default("en"),
"notifications": z.boolean().default(True)
}).optional(),
"metadata": z.object({
"source": z.literal("api"),
"version": z.string().regex(r"^\d+\.\d+\.\d+$"),
"created_at": z.datetime().default(lambda: datetime.now())
})
})
# Usage
try:
user = create_user_schema.parse(request_data)
# user is fully typed and validated
print(f"Creating user: {user['personal_info']['email']}")
except z.ZodError as e:
return {"error": "Validation failed", "details": e.flatten()}
Framework Integration
FastAPI
from fastapi import FastAPI, HTTPException
import zodic as z
app = FastAPI()
UserSchema = z.object({
"name": z.string().min(1),
"email": z.string().email(),
"age": z.number().int().min(18)
})
@app.post("/users")
async def create_user(data: dict):
try:
user = UserSchema.parse(data)
# Process validated user data
return {"user": user}
except z.ZodError as e:
raise HTTPException(status_code=422, detail=e.flatten())
Django
from django.http import JsonResponse
import zodic as z
ContactSchema = z.object({
"name": z.string().min(1).max(100),
"email": z.string().email(),
"message": z.string().min(10).max(1000)
})
def contact_view(request):
if request.method == "POST":
try:
data = ContactSchema.parse(request.POST.dict())
# Process validated data
return JsonResponse({"status": "success"})
except z.ZodError as e:
return JsonResponse({"errors": e.flatten()}, status=400)
Performance
Zodic is designed for high performance:
- 2M+ validations/second for simple schemas
- 500K+ validations/second for complex nested objects
- Zero dependencies - fast imports and minimal overhead
- Optimized error handling - detailed errors without performance cost
- Memory efficient - minimal allocations during validation
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/Seyamalam/zodic.git
cd zodic
# Install dependencies
poetry install
# Run tests
poetry run pytest
# Run quality checks
poetry run black zodic tests
poetry run isort zodic tests
poetry run mypy zodic
poetry run flake8 zodic tests
License
MIT License - see LICENSE file for details.
Changelog
See CHANGELOG.md for version history and migration guides.
Support
Star us on GitHub if Zodic helps you build better Python applications!
Metadata
Release files for zodic 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zodic-0.2.0.tar.gz | 17.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zodic-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.3 kB
Release files / zodic-0.2.0.tar.gz
| Download URL | zodic-0.2.0.tar.gz |
|---|---|
| Size | 17.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1b1d6c5cda6579e4bfe9c7c00c9c18558052aeed4e092777be0740f5122387ef
|
|
BLAKE2b-256 checksum How to use checksums |
f75e35257ae014fbb643b38d516b394a1a730e2bcde816e8d048f15d9e8e3d40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.12.4 Windows/11
|
Release files / zodic-0.2.0-py3-none-any.whl
| Download URL | zodic-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ef4ffa29a7a5dc15727635e9701fdf28a85e1310e310edad25d18ba075b45c71
|
|
BLAKE2b-256 checksum How to use checksums |
98fc7a90735202a05f386befa6461636d7381be35ede627c59c3d6571150c6cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.12.4 Windows/11
|