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.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.21.0
File Size Uploaded Provenance
polspec-0.21.0.tar.gz 780.3 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for polspec 0.21.0
File
polspec-0.21.0-cp312-abi3-win_amd64.whl CPython 3.12 abi3 Windows x86-64 Details
polspec-0.21.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.21.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.12 abi3 Linux glibc 2.17+ ARM64 Details
polspec-0.21.0-cp312-abi3-macosx_11_0_arm64.whl CPython 3.12 abi3 macOS 11.0+ ARM64 Details
polspec-0.21.0-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.0.tar.gz

Download URL polspec-0.21.0.tar.gz
Size 780.3 kB
Tags Source
SHA-256 checksum
How to use checksums
de954cac800954f790a3ad439add02af144d973c5c3a71d1129b829817807e23
BLAKE2b-256 checksum
How to use checksums
73647166933c93361631204324196a8ec67af0cd81ce27447862a0c3bb084e9f
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.0-cp312-abi3-win_amd64.whl

Download URL polspec-0.21.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
a7910d0ece3000924959f4e76959c94e72f0e5f617beda1fbc8969dc3eacf958
BLAKE2b-256 checksum
How to use checksums
82b81bdf942ee6bac7b8d5057de63359135fc2597a9cfb0bbc59d3e967e04f95
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.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL polspec-0.21.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
cd21f1c80b01d7a49714fdfc1fe829ef93fb093b6a2e5fea92e7ab8119dc051f
BLAKE2b-256 checksum
How to use checksums
ecc18592b85169da99e5a70146fc65fa1dafefa82d13645570eb6e0b0783315d
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.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL polspec-0.21.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
6c201eb481241e866865a005b3ef569eb50ec608745f1f838931a96b2fbda868
BLAKE2b-256 checksum
How to use checksums
f173378d35aa08ec5c2b596688aaaee25e511965c2663f5ce9941df23d8e06b3
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.0-cp312-abi3-macosx_11_0_arm64.whl

Download URL polspec-0.21.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
179fac7b1e3710e046248ed8c4f2f0b418aa238b040aa3b65e92cd3c0c53f3b8
BLAKE2b-256 checksum
How to use checksums
c42501b56e3ad9e27a631b98fd33ba56edd1bf7376f329245b02b43a203a551a
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.0-cp312-abi3-macosx_10_12_x86_64.whl

Download URL polspec-0.21.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
82f136670a73690c1ec3462ed6ce92717bec52cfede8a8b407c9746fd2768b2b
BLAKE2b-256 checksum
How to use checksums
cf72d35037018727becdd8f98fd57af5191efd286510731d3098b28539c9dce4
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.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.21.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 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.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