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

# 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


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.7.0.tar.gz (10.6 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.7.0-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: zspec-1.7.0.tar.gz
  • Upload date:
  • Size: 10.6 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.7.0.tar.gz
Algorithm Hash digest
SHA256 ab418cf1cbf0a43394fb0c47b1e9fcd973146dc62908207cac549170c626769f
MD5 0633e27de9170904a4ead9ddcc7da02e
BLAKE2b-256 90cc57413695e20022ddbe2a957fa4467467257ee8e3acd9d6a72bd28aab2181

See more details on using hashes here.

File details

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

File metadata

  • Download URL: zspec-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 14.6 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.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a811bf123d0f9af8452023fad3ec28e5e692301ae1bab76e4b969146c0db4c0c
MD5 3c99d81af63a5a9a7d4242e045ac4d9b
BLAKE2b-256 640c80923d3fe530cdaa9c2177f140f006fb03b4f6c2f0e8f6fc5b23ee71f46e

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