Skip to main content

polspec

Declare a Polars schema once. Generate data that matches it, and validate data against it — from the same declaration.

Early alpha. The API, the YAML format, and the exact values a given seed produces are all still moving. See Roadmap and stability before depending on any of it.

import polars as pl
from polspec import ColSpec, FrameSpec

class Orders(FrameSpec):
    order_id = ColSpec(pl.Int64, bounds=(1, None))
    status   = ColSpec(pl.Enum(["NEW", "PAID", "SHIPPED"]))
    total    = ColSpec(pl.Float64, bounds=(0.0, None))
    placed   = ColSpec(pl.Date, nullable=True)

df = Orders.generate(1_000_000, seed=42)   # a million rows in well under a second
Orders.validate(df)                        # raises ValidationError on any breach

The generator is a Rust extension that fills columns in parallel — ten million rows across four columns generates in well under 50ms on a release build. validate() compiles every check across every column into a single Polars aggregation, so validating a wide table costs about the same as a narrow one.

Why generate and validate from one declaration

A validation library tells you when production data drifted. A fixture library gives you something to test against. Keeping both behind one declaration means the fixtures and the contract can't quietly disagree — and where they still can today, it's written down in Known limitations, each backed by a test that fails the moment it's fixed.

Install

Package install (when published to PyPI)

uv add polspec           # preferred
pip install polspec      # alternative
uv add "polspec[all]"    # extras: PyArrow for Parquet/IPC

Not published to PyPI yet — install from a checkout. The generator is a compiled Rust extension, so this needs a Rust toolchain and maturin:

git clone https://github.com/MaxwellB13/polspec.git
cd polspec
uv sync --group dev        # or: pip install -e ".[all]" && pip install maturin
maturin develop --release

maturin develop builds the extension and installs the package into your active environment, editable. Writing Parquet or Arrow IPC needs PyArrow, included via [all]/the dev group above.

What you can declare

  • Types and shape — every scalar and temporal Polars dtype, nullability, bounds (including open-ended, bounds=(0, None)), string lengths, and value domains with weights.
  • Rules and invariants — conditional values (ColRule), single-column validators, multi-column checks, composite uniqueness, and foreign keys between specs.
  • Data on demand — random or coverage-guaranteeing (method="cartesian") generation, reproducible seeds, batched streaming straight to Parquet, CSV, Arrow IPC or NDJSON.
  • Specs from elsewhere — infer a spec by profiling an existing DataFrame, or load one from YAML.
  • Shared categories — a CatSpec registry so several tables agree on an Enum/Categorical domain instead of each restating it.
class Categories(CatSpec):
    STATUS   = pl.Enum(["NEW", "PAID", "SHIPPED"])
    CURRENCY = pl.Categorical(pl.Categories("CURRENCY", physical=pl.UInt8))

ColSpec(Categories.STATUS)

Command line

polspec schema infer orders.parquet -o orders.yaml   # profile data into a schema
polspec schema new Payments -o payments.py           # or start from a blank one
polspec test orders.yaml -o test_orders.py           # generate a round-trip pytest file

The generated test disables whatever validate() flags a constraint generation can't yet satisfy (unique=True, __checks__, ...) with a comment explaining why, and skips a foreign key that needs parent data it wasn't given — see Command line.

Documentation

Full docs: maxwellb13.github.io/polspec

To edit and preview the docs locally instead, see zensical:

zensical serve

Benchmarks — polspec vs NumPy vs pure Python

The benchmarks/bench_generate.py script compares the Rust-backed generator to:

  • a NumPy-based generator that builds equivalent columns, and
  • a pure-Python implementation using random.

Run locally:

uv run --group bench python benchmarks/bench_generate.py

Example results (illustrative; your hardware will differ):

n_rows polspec (Rust) NumPy Python Polspec v NumPy Speedup
1,000 0.0003s 0.0008s 0.016s 2.6x
10,000 0.0007s 0.0052s 0.153s 8.4x
100,000 0.0026s 0.0488s 0.900s 18.7x
1,000,000 0.0085s 0.4836s 1.4391s 57.8x
5,000,000 0.0318s 2.4141s skipped 75.9x
20,000,000 0.0850s 9.6097s skipped 113.0x

Ran on Intel 13900K + 64GB DDR5 RAM

Notes

  • All three implementations emit a Polars DataFrame with the same schema to keep the comparison fair. The NumPy version uses a fixed-width trick for strings (since vectorized ragged strings are not available), and the pure-Python version builds lists then constructs a DataFrame.
  • The script warms each implementation once, then times increasing sizes; the pure-Python path is skipped for very large sizes once it exceeds a cutoff.

Development

After the install steps above:

pytest
ruff check . && ruff format --check .
cargo clippy --release

License

Not yet specified.

Release files for polspec 0.1.2

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

Built distributions (wheels)

Table of built distributions (wheels) for polspec 0.1.2
File Interpreter ABI Platform
polspec-0.1.2-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
polspec-0.1.2-cp311-abi3-manylinux_2_34_x86_64.whl CPython 3.11 abi3 Linux glibc 2.34+ x86-64 Details

Total release size: 12.9 MB

Release files / polspec-0.1.2-cp311-abi3-win_amd64.whl

Download URL polspec-0.1.2-cp311-abi3-win_amd64.whl
Size 6.3 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
72b04b257d9fde6a78ddd702e197bceb3ad6bfdd46e88aba231de3eff74eb3ab
BLAKE2b-256 checksum
How to use checksums
52eddfb59433ad6f094d94d64264f7cd4462f469cda63a1626c7a293a7bbccb4
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 Aug 31, 2026.

Transparency log

Release files / polspec-0.1.2-cp311-abi3-manylinux_2_34_x86_64.whl

Download URL polspec-0.1.2-cp311-abi3-manylinux_2_34_x86_64.whl
Size 6.6 MB
Tags CPython 3.11 Linux glibc 2.34+ x86-64 abi3
SHA-256 checksum
How to use checksums
64912ff228d190cab9ddb1a446dc764c78dba6af12015dc951c5416b5ccdb4e6
BLAKE2b-256 checksum
How to use checksums
2db026e5dde7472179cc00c6f293cf4003b209e3fec01e60a8bdd310fefc64b2
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 Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.1

6 release files

0.9.0

6 release files

0.8.0

6 release files

0.7.0

6 release files

0.6.0

6 release files

0.5.0

6 release files

0.4.1

6 release files

0.4.0

6 release files

0.3.0

6 release files

0.2.0

6 release files

0.1.5

6 release files

0.1.4

2 release files

0.1.3

2 release files

This release

0.1.2 This release

2 release files

0.1.0

2 release files

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