Skip to main content

Pydantic-native Apache Arrow DataFrame wrapper

Project description

pydantic-arrow

Tests Lint Memory Python pre-commit Ruff

A Pydantic-native lazy Apache Arrow DataFrame wrapper.

Define your data schema once as a Pydantic model and get a fully typed, lazily-evaluated Arrow-backed frame -- streaming data batch-by-batch without ever loading the full dataset into memory.

Features

  • Pydantic-first -- derive Arrow schemas directly from BaseModel field annotations.
  • Lazy evaluation -- data flows as RecordBatch streams; nothing is materialised until you ask for it.
  • Multiple sources -- rows/dicts, Parquet files, existing pa.Table / pa.RecordBatchReader.
  • Rich type mapping -- covers str, int, float, bool, bytes, datetime, date, time, Decimal, UUID, list[T], dict[K,V], Optional[T], Literal, Enum, and nested BaseModel structs.
  • Python 3.11+ with typing-inspection for safe introspection across Python versions.

Installation

pip install pydantic-arrow

Or with uv:

uv add pydantic-arrow

Quick start

from pydantic import BaseModel
from pydantic_arrow import ArrowFrame


class User(BaseModel):
    name: str
    age: int
    email: str | None = None


# Build from rows -- never loaded all at once
users = ArrowFrame[User].from_rows([
    {"name": "Alice", "age": 30},
    {"name": "Bob",   "age": 25, "email": "bob@example.com"},
])

# Iterate lazily -- yields validated User instances, batch-by-batch
for user in users:
    print(user.name, user.age)

# Collect everything (materialises into memory)
all_users: list[User] = users.collect()

# Single row / slice (only materialises what you ask for)
first: User           = users[0]
page:  ArrowFrame     = users[10:20]

Reading Parquet files

# Streams row-groups lazily -- the file is never fully loaded
users = ArrowFrame[User].from_parquet("users.parquet")

for user in users:           # validated User instances, one batch at a time
    process(user)

users.to_parquet("copy.parquet")  # incremental write, also lazy

Wrapping existing Arrow data

import pyarrow as pa

# from a pa.Table
table: pa.Table = ...
users = ArrowFrame[User].from_arrow(table)

# from a pa.RecordBatchReader (one-shot stream)
reader: pa.RecordBatchReader = ...
users = ArrowFrame[User].from_arrow(reader)

Low-level batch access

for batch in users.iter_batches():   # pa.RecordBatch
    ...

Exporting

table = users.to_arrow()           # materialise as pa.Table
rows  = users.collect()            # list[User]
users.to_parquet("out.parquet")    # streaming write, no full materialisation

Schema

The Arrow schema is derived automatically from the Pydantic model:

from pydantic_arrow import model_to_schema

print(model_to_schema(User))
# name: string not null
# age: int64 not null
# email: string

Type mapping

Python / Pydantic Arrow
str pa.utf8()
int pa.int64()
float pa.float64()
bool pa.bool_()
bytes pa.binary()
datetime pa.timestamp("us")
date pa.date32()
time pa.time64("us")
Decimal (with max_digits/decimal_places) pa.decimal128(p, s)
UUID pa.utf8() (stored as canonical UUID string)
list[X] pa.list_(arrow(X))
dict[K, V] pa.map_(arrow(K), arrow(V))
T | None / Optional[T] same type, nullable
Literal["a", "b"] pa.dictionary(pa.int32(), pa.utf8())
Enum(str) pa.dictionary(pa.int32(), pa.utf8())
Enum(int) pa.int64()
nested BaseModel pa.struct(...) (recursive)

Development

# Install all dev dependencies
make install

# Run tests
make test

# Run tests in parallel
pytest -n auto

# Auto-fill/update inline snapshots
pytest --inline-snapshot=fix

Memory guarantees

ArrowFrame is designed so that at most one RecordBatch (one Parquet row group) is live in the Arrow memory pool at any time during iteration. This is verified by two complementary test strategies in tests/test_memory.py:

  • pa.total_allocated_bytes() assertions (always active): prove that the Arrow C++ allocator peak stays well below the full table size.
  • pytest-memray limit_memory markers (enforced with --memray): set a hard 2 MB cap on tests that stream a 200 K-row / 5+ MB dataset.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydantic_arrow-1.1.0.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydantic_arrow-1.1.0-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file pydantic_arrow-1.1.0.tar.gz.

File metadata

  • Download URL: pydantic_arrow-1.1.0.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pydantic_arrow-1.1.0.tar.gz
Algorithm Hash digest
SHA256 745a59e985aff17aece605908b6b91f48a3a2b8e31b5e5fce1e1d86914414338
MD5 4e5291ce00277652239602bb221097eb
BLAKE2b-256 fb34bbf877b608c2bc44418755cf40d0a9e669e640b012aa5c8a8a311a52ac1d

See more details on using hashes here.

File details

Details for the file pydantic_arrow-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: pydantic_arrow-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pydantic_arrow-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b8894f3b1d8ec610319e6ee453b915f29598f6657eb232a91671520a6229da82
MD5 fface6c23efd3dd1de51ad7ca90bb0c9
BLAKE2b-256 71e14671533c6f6e9a70ee046da8c5794c87881015993c5c798a8fcbebf8e4fa

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page