Skip to main content

Composable specification pattern for Python

Project description

zspec

PyPI Python CI Docs License

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, registered

@registered
class InStock(Specification[Product]):
    ...

# Save
json.dump(to_dict(InStock() & MinPrice(100)), f)

# Load — @registered 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


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.6.0.tar.gz (10.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

zspec-1.6.0-py3-none-any.whl (14.7 kB view details)

Uploaded Python 3

File details

Details for the file zspec-1.6.0.tar.gz.

File metadata

  • Download URL: zspec-1.6.0.tar.gz
  • Upload date:
  • Size: 10.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","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

Hashes for zspec-1.6.0.tar.gz
Algorithm Hash digest
SHA256 a56787b8b881879f40d49685dd02c0b9f2bbcf3e50653662c356c1a5ce1668f5
MD5 6123b603ff0199a89f3049bd3f98043f
BLAKE2b-256 9835af7428bfe0ab7f499f0488ae27cb250b895cd359bac990552a2fc13bfaf1

See more details on using hashes here.

File details

Details for the file zspec-1.6.0-py3-none-any.whl.

File metadata

  • Download URL: zspec-1.6.0-py3-none-any.whl
  • Upload date:
  • Size: 14.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","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

Hashes for zspec-1.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7ac01e20e7e5e1d2b7691945cd2a0e7a5ac7d08336e4140e03b61afd9a96e2f
MD5 5642183e0d0113754e03a18722eda203
BLAKE2b-256 0407288d8e9fdcc682c3594cd8e3f30e9f8042b3980291cc0ffbfd8910e157e4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page