Composable specification pattern for Python
Project description
zspec
Composable specification pattern for Python 3.12+.
Documentation · PyPI · GitHub
Install
pip install zspec
Quick start
from dataclasses import dataclass
from zspec import Specification
@dataclass
class Product:
price: int
in_stock: bool
# Define rules as classes
class InStock(Specification[Product]):
def is_satisfied_by(self, p: Product) -> bool:
return p.in_stock
class MinPrice(Specification[Product]):
def __init__(self, threshold: int) -> None:
self.threshold = threshold
def is_satisfied_by(self, p: Product) -> bool:
return p.price >= self.threshold
# Compose with &, |, ^, ~
eligible = InStock() & MinPrice(100)
product = Product(price=200, in_stock=True)
assert eligible(product)
Or skip the class boilerplate:
# Field comparisons
spec = Specification[Product].matching(price__gte=100, in_stock=True)
# Lambda predicates
spec = Specification[Product].matching(
lambda p: p.price > 100,
lambda p: p.in_stock,
)
Why zspec?
| Composable | & | ^ ~ — build complex rules from simple ones without new classes |
| Zero dependencies | Standard library only. Optional extras for SQLAlchemy, Django, Polars, and Pandas |
| Type-safe | Generic Specification[T] preserves candidate types through composition |
| Database translators | One spec → SQL, MongoDB, Django Q, SQLAlchemy, Polars, or Pandas expression |
| Serializable | to_dict() / from_dict() — store rules in JSON configs or databases |
| Debuggable | explain() prints a PASS / FAIL tree for every node |
Filtering collections
passed = list(eligible.filter(products)) # lazy generator
failed = list(eligible.reject(products)) # inverse
passed, failed = eligible.partition(products)
count = eligible.count(products)
Debugging
from zspec import explain
print(explain(eligible, product))
# AND FAIL
# ├── InStock PASS
# └── price >= 100 FAIL
Serialization
from zspec import to_dict, from_dict
# Save
json.dump(to_dict(InStock() & MinPrice(100)), f)
# Load — specs are auto-discovered
spec = from_dict(json.load(f))
Translators
One spec — query any backend:
# SQL
MySql().translate(eligible) # SqlFragment("price >= %s AND in_stock", (100, True))
# MongoDB
MyMongo().translate(eligible) # {"$and": [{...}, {...}]}
# Django
MyDjango().translate(eligible) # Q(price__gte=100) & Q(in_stock=True)
# SQLAlchemy
MySA().translate(eligible) # ColumnElement[bool]
# Polars
MyPolars().translate(eligible) # pl.Expr
# Pandas
MyPandas().translate(eligible) # query string
License
MIT
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
zspec-1.8.2.tar.gz
(12.7 kB
view details)
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
zspec-1.8.2-py3-none-any.whl
(18.6 kB
view details)
File details
Details for the file zspec-1.8.2.tar.gz.
File metadata
- Download URL: zspec-1.8.2.tar.gz
- Upload date:
- Size: 12.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d6db186c0c5d56cb84bfe3296f50129e577659465251080662d026a4e9f2d29
|
|
| MD5 |
054b5861c3eb68ca2d8ee80665f001a6
|
|
| BLAKE2b-256 |
354125c180ea375bc4e1e9574b15dbb14f59eb061eeb7a17487e0cf84e590550
|
File details
Details for the file zspec-1.8.2-py3-none-any.whl.
File metadata
- Download URL: zspec-1.8.2-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32fbbcf40d4595c1f49e8c7111d51979aa98690da1243ebd6a774b0a13a69982
|
|
| MD5 |
6099e4e2d5c4281f9245908005ae3899
|
|
| BLAKE2b-256 |
39a6bb58f9fee697781c792cccc54401467c52651838ef2ba87e1b57027a58c6
|