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.15 (on 3.15, Polars 2: Polars 1 does not run on it). 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.21.1

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.21.1
File Size Uploaded Provenance
polspec-0.21.1.tar.gz 784.2 kB With provenance Details

Built distributions (wheels)

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

Total release size: 31.1 MB

Release files / polspec-0.21.1.tar.gz

Download URL polspec-0.21.1.tar.gz
Size 784.2 kB
Tags Source
SHA-256 checksum
How to use checksums
cbdda33116378828a5424a9c430cd9979018b8d41a81d42370e854d1836447f1
BLAKE2b-256 checksum
How to use checksums
44970b9b08b85674cf77864adc875e46cb7f7308453f53f3cc944759494ee243
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 Oct 22, 2026.

Transparency log

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

Download URL polspec-0.21.1-cp312-abi3-win_amd64.whl
Size 6.6 MB
Tags CPython 3.12 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
c8b45735d0fd75c1510bac5da329a3e7ba0a12c1a2d4b2682e4ffbd8b9798f50
BLAKE2b-256 checksum
How to use checksums
1b888958fe01b2265c5b6adf365949a4940ca6d7e534b7fb85c7be024f255abc
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 Oct 22, 2026.

Transparency log

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

Download URL polspec-0.21.1-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
13dd3fcf36b27f210fa5ba175d848c6ef649db24c0aebfe2d3f03dd094b486f2
BLAKE2b-256 checksum
How to use checksums
ee47031c05c0092aa0baa03b7eb9e540c9dc9ca8dd7a84e605d2a49d34dbbf78
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 Oct 22, 2026.

Transparency log

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

Download URL polspec-0.21.1-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
82ba60fe5a0c9862435f28ae8b297214c6fcb3af9cccc23fc737a9d75c44f85a
BLAKE2b-256 checksum
How to use checksums
ed9cc320342c649ba27d34e724db7b38feaf7c08053f365c37c3ffb3b390c211
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 Oct 22, 2026.

Transparency log

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

Download URL polspec-0.21.1-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
88be5203f3c1df45676f926a84ad15445f23378e4928b41a2f199cb7b2e55dad
BLAKE2b-256 checksum
How to use checksums
2c586956450cde00765977293c288312bf1313111c084abdbc2a2fac589dfb5e
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 Oct 22, 2026.

Transparency log

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

Download URL polspec-0.21.1-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
b022f7825a39036152f0314a89d24173be266d3558c0bc5fecb9aabd6958297a
BLAKE2b-256 checksum
How to use checksums
8b8bf5e151e844756da8aa705246d3439971ef5fedc70c27cf43f9e0afc6c591
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 Oct 22, 2026.

Transparency log

Security for polspec 0.21.1

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.21.1.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 22, 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.21.1 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