Skip to main content

sverify

Prove a data pipeline's contracts before it runs, get the same result on DuckDB, Polars, DataFusion, Spark and Snowflake, and keep it up to date from Delta Lake and Iceberg change feeds.

You write a transformation in Ibis or SQL and say what it assumes about its inputs (requires) and what it guarantees about its output (ensures). A checker written in Lean proves the guarantees for every possible input, or says why it can't. Then:

  • Contracts are proved, not tested. not_null, unique, ranges and relations between columns hold on every input that meets the preconditions, and division by zero, overflow and bad casts are ruled out before the job runs. Preconditions are checked on the data, or proved from Delta and Iceberg metadata.
  • One plan gives the same answer on every engine. The checked plan runs on DuckDB, Polars or DataFusion, or is written as Snowflake or Spark SQL, with NULLs, integer division, NaN, ties and overflow spelled out, or it is refused where an engine can't match.
  • Results are kept up to date from the changes, by delta plans proved equal to a full recompute, read from Delta's Change Data Feed or Iceberg's appends, and written to sinks exactly once, with the contract attached.
pip install sverify                # or: uv add sverify
pip install 'sverify[all]'         # with every engine and table format

The core runs on DuckDB and writes DuckDB, Snowflake and Spark SQL. Extras: datafusion (also compiles SQL strings), polars, spark (needs Java 17 or 21), delta, iceberg, or all. Wheels bundle the checker as a small native binary, for Linux (x86_64, aarch64) and macOS (arm64, x86_64), Python 3.12 and later; elsewhere pip builds from the sdist, which needs elan.

The API reference documents every public function and class, for the released version. For coding agents, the site also publishes skills for writing contracts, pipelines and engine code with sverify (npx skills add https://alexandernenninger.github.io/substrait-lean/); they ship in the wheel too, under sverify/skills/.

In thirty lines

import pyarrow as pa
import sverify as sv
from ibis import _
from sverify.io import StaticSource

orders = StaticSource(  # in memory; DeltaSource and IcebergSource work the same way
    pa.table({"id": [1, 2], "customer": ["ada", "bob"], "amount": [30, 5]}), name="orders"
)


@sv.transform(
    requires={"orders": [_.amount >= 0, sv.unique("id")]},
    ensures=[_.doubled >= 22, _.doubled.notnull(), sv.unique("id")],
)
def big_orders(orders):
    return orders.filter(orders.amount > 10).select(orders.id, doubled=orders.amount * 2)


big = big_orders(orders)  # proved here, before any data runs
big.refresh()  # computed, on DuckDB by default
orders.apply(sv.Changes(inserts=[(3, "cy", 50)], deletes=[(1, "ada", 30)]))
big.refresh(engine="polars")  # updated from the changes alone, on another engine
assert big.to_arrow().to_pylist() == [{"id": 3, "doubled": 100}]
print(big_orders.last_report)
# requires orders: (_.amount >= 0)
# requires orders: unique(id)
# ensures (_.doubled >= 22): proved

A postcondition that doesn't follow is refused before anything runs:

@sv.transform(requires={"orders": [_.amount >= 0]}, ensures=[_.total > 5])
def plus_one(orders):
    return orders.select(orders.id, total=orders.amount + 1)


from sverify.errors import VerificationError

try:
    plus_one(orders)
except VerificationError as e:
    print(e)
# could not prove: (_.total > 5) (it is TRUE or FALSE)

SQL models

A transform's body can be SQL, over its inputs by name. It is proved, composed and refreshed like any other:

@sv.transform(
    requires={"orders": ["amount >= 0", "amount IS NOT NULL"]},
    ensures=["total >= 0", sv.unique("customer")],
)
def per_customer(orders):
    return sv.sql(
        "SELECT customer, coalesce(sum(amount), 0) AS total FROM orders GROUP BY customer",
        orders=orders,
    )


totals = per_customer(orders)  # proved here, like an Ibis transform
totals.refresh()
assert sorted(totals.to_arrow().to_pylist(), key=str) == [
    {"customer": "bob", "total": 5},
    {"customer": "cy", "total": 50},
]

Several inputs are several keywords (sv.sql(query, orders=orders, customers=customers)). The SQL is planned by DataFusion (pip install 'sverify[datafusion]'), or by Isthmus without it.

Workflows

Three complete jobs from a catalogue of 139 real analytical queries, each from source tables to a sink, with what goes wrong without the checker. Open them in the browser:

  • Duplicate card payments: a self-join over a Delta table, refreshed from its Change Data Feed, where a watermark job would miss a late duplicate and keep a reversed one.
  • Sensor alerts: LAG over an Iceberg table, where ties and a NaN reading change the alerts, and Snowflake and Spark need proved tie-freedom.
  • An orders mart: dbt tests as a proved contract on a point-in-time join to a type-2 dimension, in Ibis and in SQL.

The whole tour explains every feature in a chapter of its own; it runs locally with uvx marimo edit --sandbox notebooks from a clone.

The API in brief

import json

import ibis
from sverify.engines import SNOWFLAKE, to_sql  # also DUCKDB, SPARK
from sverify.io import DeltaSink, DeltaSource  # IcebergSource, IcebergSink likewise

sv.verify(
    "select id, amount from orders where amount > 0",
    tables={"orders": ibis.table({"id": "int64", "amount": "int64"}, name="orders")},
    ensures=["amount > 0"],
)  # SQL, checked; returns the report
to_sql(json.loads(big.plan), SNOWFLAKE)  # the checked plan, or sverify.engines.Unsupported

# p = paid(DeltaSource("s3://.../orders")).write(DeltaSink("s3://.../paid"))  # stored: a source
# r = revenue(p).write(DeltaSink("s3://.../revenue"))
# r.refresh(engine="polars"); sv.group(r, other).refresh(); r.to_pandas()
  • @sv.transform(requires=..., ensures=..., on_unproved="error" | "runtime" | "warn") proves a contract on each call; .last_report says what was proved and what is known about every output column. sv.unique(...) states keys.
  • Called on sources (sverify.io) or other nodes, transforms build nodes that compose directly: a downstream precondition is proved from the upstream contract (CompositionError names a broken link).
  • Without .write in between, transforms fuse into one plan; .write(sink) stores a node, exactly once per state, and nodes downstream read it as a versioned source. refresh(engine=...) updates the whole lineage, and sv.group(a, b) several outputs against the same source versions.
  • Filters, projections, inner and cross joins and UNION ALL are maintained from the changes; anything else is recomputed and diffed.
  • sverify.engines has the engines, SQL dialects and plans (run_sharded runs a plan per shard and combines the results, with a proof that the result is the same); sverify.errors the exceptions.

Not supported yet: DECIMAL columns (keep money as integer cents), framed window aggregates, RANK and NTILE. The repository's README has the coverage table, the full guide, and what is proved and what is trusted.

Configuration

  • SVERIFY_CHECK: path to a substrait-check binary to use instead of the bundled one, for example your own lake build.
  • SVERIFY_ISTHMUS: path to an Isthmus binary (otherwise isthmus on PATH), to compile SQL strings without DataFusion; sv.connect(sql_producer="isthmus") picks it, and it is the default when DataFusion is not installed. sverify doesn't ship it (~120 MB); scripts/fetch_isthmus.sh <dir> downloads the pinned release.
  • DataFusion is pinned to 51, because Ibis 12's DataFusion backend doesn't connect to later versions.

Development

git clone https://github.com/AlexanderNenninger/substrait-lean
cd substrait-lean/python
uv sync                          # builds substrait-check with lake (needs elan) and links it in
uv run poe qa                    # proofs, lint, typecheck, golden, Python tests, notebooks

See the repository for setup details.

Metadata

Release files for sverify 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sverify 0.2.0
File Size Uploaded Provenance Actions
sverify-0.2.0.tar.gz 777.2 kB With provenance Details for sverify-0.2.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for sverify 0.2.0
File Actions
sverify-0.2.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for sverify-0.2.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
sverify-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for sverify-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
sverify-0.2.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for sverify-0.2.0-py3-none-macosx_11_0_arm64.whl
sverify-0.2.0-py3-none-macosx_10_15_x86_64.whl Python 3 none macOS 10.15+ x86-64 Details for sverify-0.2.0-py3-none-macosx_10_15_x86_64.whl

Total release size: 116.9 MB

Release files / sverify-0.2.0.tar.gz

Download URL sverify-0.2.0.tar.gz
Size 777.2 kB
Tags Source
SHA-256 checksum
How to use checksums
c40aa68cb50c0e624ee574b0574ec2f1c9ca940b140f768375d7c8b2a06548e6
BLAKE2b-256 checksum
How to use checksums
57651de12ccbf68ee7b38d4948828885390bbd2d6a87e16da421f74ebe3f33f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / sverify-0.2.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL sverify-0.2.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 29.9 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
af4f2dd41125dcd1b82929420ad42bdc3bbd9cb7a58d36f91b56f8d583ddc0b0
BLAKE2b-256 checksum
How to use checksums
2faf0648a8e8ada20853724e44ed0a57cce1e58a2d288a7d84081e834d20acef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / sverify-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL sverify-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 29.8 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
bfee6a7377caf615e24f0ce456e31d91a298643eab20bc82c874d5957662c49b
BLAKE2b-256 checksum
How to use checksums
008779854d2bee1df9eab1e202ee69d70569f8536ffcf50bf9c04be75efd44df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / sverify-0.2.0-py3-none-macosx_11_0_arm64.whl

Download URL sverify-0.2.0-py3-none-macosx_11_0_arm64.whl
Size 27.3 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
39a733aba850300c169b2ed0ff7c8ae76f43f54e27e222bb8ba3b87090dc6cc8
BLAKE2b-256 checksum
How to use checksums
18d7c5deb58aef8985b4765132c483d20622cc0b4a0d74cedb5ea4d46190a162
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Release files / sverify-0.2.0-py3-none-macosx_10_15_x86_64.whl

Download URL sverify-0.2.0-py3-none-macosx_10_15_x86_64.whl
Size 29.1 MB
Tags Python 3 macOS 10.15+ x86-64
SHA-256 checksum
How to use checksums
fe2292d409fd61a9504dc45bf9ea80d87ea13726f306bfddbccf7e47975726d0
BLAKE2b-256 checksum
How to use checksums
ffb8a833907fcaf3721fc9d52c354f16539add1236839c8b762074b026402cac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

Security for sverify 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 5 files in this release have been uploaded with provenance metadata.

Select from 5 files

Attestation for sverify-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page