Skip to main content

polspec

One schema for your Polars data. Generate it, validate it, watch it drift.

PyPI Python Tests License

Your tests need fixtures, and your pipeline needs a contract. Written separately, they drift apart. polspec lets you write the schema once, as a Polars class, and use it for both: realistic data to test against, and validation of the data that turns up.

import datetime as dt

import polars as pl
from polspec import ColSpec, FrameSpec

class Orders(FrameSpec):
    order_id = ColSpec(pl.Int64, bounds=(1, 10**9), unique=True)
    status   = ColSpec(pl.Enum(["NEW", "PAID", "SHIPPED"]), weights=[0.2, 0.5, 0.3])
    total    = ColSpec(pl.Float64, bounds=(0, 5_000), distribution="lognormal",
                       distribution_params={"mean": 4, "std": 1})
    placed   = ColSpec(pl.Date, bounds=(dt.date(2025, 1, 1), dt.date(2025, 12, 31)),
                       nullable=True, null_probability=0.05)
    email    = ColSpec(pl.String, format="email")

orders = Orders.generate(1_000_000, seed=42)   # a million rows in ~12 ms
Orders.validate(orders)                        # ~27 ms; raises on any breach
┌───────────┬────────┬────────────┬────────────┬───────────────────────┐
│ order_id  ┆ status ┆ total      ┆ placed     ┆ email                 │
│ i64       ┆ enum   ┆ f64        ┆ date       ┆ str                   │
╞═══════════╪════════╪════════════╪════════════╪═══════════════════════╡
│ 830916752 ┆ PAID   ┆ 9.187332   ┆ 2025-12-30 ┆ dupebca@xdcbivwq.dev  │
│ 366447256 ┆ PAID   ┆ 19.02604   ┆ 2025-11-28 ┆ hinro@ewzc.com        │
│ 922459406 ┆ PAID   ┆ 336.930617 ┆ 2025-04-11 ┆ jp5uo7@mgayhywoxn.com │
└───────────┴────────┴────────────┴────────────┴───────────────────────┘

What you get

  • Fast. The generator is a Rust extension that fills columns in parallel. validate() compiles every check on every column into one Polars aggregation, so a wide table costs about what a narrow one does. Timings here are from a laptop; more are in Comparison.
  • Lazy and streaming. scan() is a LazyFrame: filter and project it like any other. The sinks write Parquet, IPC, CSV or NDJSON in batches, never holding the frame in memory, and a unique column stays unique across every batch.
  • Every Polars dtype. Integers to 128 bits, Decimal, time-zoned Datetime, Enum, Categorical, and List, Array and Struct nested to any depth (plus Map on Polars 2). Bounds, choices with weights, distributions, formats such as email and uuid4, uniqueness, nulls and NaNs are all declared per column.
  • Tables that relate. Foreign keys within and across specs, conditional rules, composite keys and hierarchies are generated to hold, and validated. Checks written as Polars expressions are enforced at validation.
  • Validation as data. inspect() returns every finding at once, with the offending rows a lazy filter away, and splits a frame into what passed and what to quarantine.
  • Learn from real data. profile() turns a frame into a spec, finding formats, frequencies, distributions and null rates; synthesize() makes a stand-in with the same shape.
  • Drift with statistics behind it. A move is reported only when it is both significant and large, as breaking or compatible.
  • Specs as files and docs. Save and load specs as YAML or Python. Render a Markdown data dictionary or a Mermaid ER diagram.
  • A CLI for your pipeline. polspec validate, generate, drift and synthesize, with exit codes for CI.
  • Deterministic. The same seed gives the same data on every Polars from 1.39 to 2.x, on Windows, Linux and macOS, checked case by case in CI.

A quick tour

Generate lazily

scan() returns a LazyFrame, so Polars pushes filters and projections into it:

paid = (
    Orders.scan(10_000_000, seed=1)
    .filter(pl.col("status") == "PAID")
    .select("order_id", "total")
    .collect()                                            # ~0.2 s
)
Orders.sink_parquet("orders.parquet", 1_000_000, seed=1)  # streamed in batches

Validate and quarantine

inspect() never raises: it reports every finding, with the rows behind it.

broken = orders.with_columns(
    pl.when(pl.col("order_id") % 1_000 == 0).then(-1.0).otherwise("total").alias("total")
)
report = Orders.inspect(broken)          # 1 finding: 1,053 totals out of bounds
clean = report.passing_rows().collect()  # the other 998,947 rows

Profile real data

from polspec import profile, synthesize

spec = profile(orders.head(100_000))
spec["total"].distribution                         # 'lognormal', fitted
fake = synthesize(orders.head(100_000), seed=1)    # a stand-in with the same shape

Catch drift

print(Orders.drift(orders.filter(pl.col("status") != "NEW")))
Drift: 0 breaking, 2 compatible, DataFrame against 'Orders'
  - [compatible] Column 'status': 1 of 3 declared value(s) never appear: ['NEW']
  - [compatible] Column 'status': frequencies moved by 20.0% over 800,231 value(s): ...

From the command line

polspec schema infer orders.csv -o orders_spec.py
polspec validate orders_spec.py orders.csv --failing rejected.csv
polspec drift orders_spec.py orders.csv --markdown
polspec generate orders_spec.py -n 1000000 -o fixture.parquet --seed 1

Install

uv add polspec           # or: pip install polspec

polspec works with Polars 1.39 and later, Polars 2 included, on Python 3.12 to 3.14. Wheels are published for Linux (x86_64, aarch64), macOS (Intel and Apple silicon) and Windows (x86_64), so you don't need a Rust toolchain.

Pre-1.0. The API is settling toward a 1.0 with written stability promises. Until then, see Roadmap and stability, and Known limitations for what polspec does not do. Each limitation there is backed by a test that fails the moment it stops being true.

Documentation

maxwellb13.github.io/polspec: start with Getting started, find a task among the how-to guides, or look a name up in the API reference.

Contributing

Setting up a checkout, the checks CI runs and the release process are in CONTRIBUTING.md. What changed, and when, is in CHANGELOG.md.

License

MIT.

Metadata

Release files for polspec 0.19.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 polspec 0.19.0
File Size Uploaded Provenance
polspec-0.19.0.tar.gz 747.2 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for polspec 0.19.0
File
polspec-0.19.0-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
polspec-0.19.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.12 abi3 Linux glibc 2.17+ x86-64 Details
polspec-0.19.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
polspec-0.19.0-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details
polspec-0.19.0-cp312-abi3-macosx_10_12_x86_64.whl CPython 3.12 abi3 macOS 10.12+ x86-64 Details

Total release size: 31.0 MB

Release files / polspec-0.19.0.tar.gz

Download URL polspec-0.19.0.tar.gz
Size 747.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4230f1a9efb1622a2df35b9523b79ba90d726dacaa06e98c24f0b609b46f9b57
BLAKE2b-256 checksum
How to use checksums
1c7f38f50b8a1e475d6bcce906f227a51220ccfd719b64774a9e3e91911be25c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Release files / polspec-0.19.0-cp312-abi3-win_amd64.whl

Download URL polspec-0.19.0-cp312-abi3-win_amd64.whl
Size 6.6 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
8203d0f8bb1d8e02d6e1e5d7050ed56de07e2cadba6fb50e978b7d956ba14b94
BLAKE2b-256 checksum
How to use checksums
860f84b159833b32ca6be285599b9479496615c0c7ee9ed53d2d1fd7e856977b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Release files / polspec-0.19.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL polspec-0.19.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 6.3 MB
Tags CPython 3.12 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
8fe685b2e8ba670dbd31e6d3023e57c6732e8ad74b02ca8f4f1bb13761605440
BLAKE2b-256 checksum
How to use checksums
e2c02117d49be18146fd76aec1e30b3afcebf10e8e43a20465f24f12c78a6fc0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Release files / polspec-0.19.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL polspec-0.19.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 5.8 MB
Tags CPython 3.12 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
971fc59aec19e6f62736fd05b71bcb3d071c153eceb46f3ae116b71879d397d6
BLAKE2b-256 checksum
How to use checksums
a1f4673c92a82c89aa32a449461676a4a1e46a4b8519a5565ace3850d3d26d19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Release files / polspec-0.19.0-cp312-abi3-macosx_11_0_arm64.whl

Download URL polspec-0.19.0-cp312-abi3-macosx_11_0_arm64.whl
Size 5.5 MB
Tags CPython 3.12 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a5bba91fbd381281aef7e58744d19fb995bba48d5f6e8c8c8d7bf92b5f0562c3
BLAKE2b-256 checksum
How to use checksums
848ad4644e3d1e9f96e902e40b1116b1c78a11647ab2afcab49a3e8e2b7a752d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Release files / polspec-0.19.0-cp312-abi3-macosx_10_12_x86_64.whl

Download URL polspec-0.19.0-cp312-abi3-macosx_10_12_x86_64.whl
Size 6.2 MB
Tags CPython 3.12 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
d902a40987d3d77fd3a9553866e6d3ebfbe9a473bb7f0ede5a091c07b40677d5
BLAKE2b-256 checksum
How to use checksums
45896e24790c43a8536f200bbc7009b4eef05fc11d33f95c395a34ad3a16a0f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Oct 16, 2026.

Transparency log

Security for polspec 0.19.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 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for polspec-0.19.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 Oct 16, 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.

Release history Release notifications | RSS feed

This release

0.19.0 This release

6 release files Full provenance

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