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.8.2.tar.gz (12.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.8.2-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

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

Hashes for zspec-1.8.2.tar.gz
Algorithm Hash digest
SHA256 1d6db186c0c5d56cb84bfe3296f50129e577659465251080662d026a4e9f2d29
MD5 054b5861c3eb68ca2d8ee80665f001a6
BLAKE2b-256 354125c180ea375bc4e1e9574b15dbb14f59eb061eeb7a17487e0cf84e590550

See more details on using hashes here.

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

Hashes for zspec-1.8.2-py3-none-any.whl
Algorithm Hash digest
SHA256 32fbbcf40d4595c1f49e8c7111d51979aa98690da1243ebd6a774b0a13a69982
MD5 6099e4e2d5c4281f9245908005ae3899
BLAKE2b-256 39a6bb58f9fee697781c792cccc54401467c52651838ef2ba87e1b57027a58c6

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