A TypeScript Zod-inspired validation library for Python with excellent type safety and developer experience
Project description
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!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zodic-0.2.0.tar.gz.
File metadata
- Download URL: zodic-0.2.0.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.12.4 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b1d6c5cda6579e4bfe9c7c00c9c18558052aeed4e092777be0740f5122387ef
|
|
| MD5 |
8edec47b619f4c46de219bcc16c621d3
|
|
| BLAKE2b-256 |
f75e35257ae014fbb643b38d516b394a1a730e2bcde816e8d048f15d9e8e3d40
|
File details
Details for the file zodic-0.2.0-py3-none-any.whl.
File metadata
- Download URL: zodic-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.3 CPython/3.12.4 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef4ffa29a7a5dc15727635e9701fdf28a85e1310e310edad25d18ba075b45c71
|
|
| MD5 |
f1af86c96b1007d11a47949dfa07651e
|
|
| BLAKE2b-256 |
98fc7a90735202a05f386befa6461636d7381be35ede627c59c3d6571150c6cb
|