A typed framework for writing Ibis expressions with full IDE and static-analysis support
Project description
ibis-typing
A typed framework for writing Ibis dataframe expressions — with full IDE support, static analysis, and property-based testing.
Ibis is a portable Python dataframe library (DSL) that runs on DuckDB, Polars, Trino, BigQuery, and more. ibis-typing layers a type-safe schema system on top of it, so your transforms carry type information end-to-end.
Installation
pip install ibis-typing
uv add ibis-typing
After installation, run the type-patch step once to inject typed overloads into your installed ibis package:
python -m ibis_typing.type_patch
Quick start
1. Define schemas
from attrs import frozen
from ibis_typing import IbisSchema, it
@frozen
class Transaction(IbisSchema):
date: it.Date = None
amount: it.Float64 = None
category: it.String = None
2. Define a typed expression
from ibis_typing import Expression, IbisTable, this, it
@frozen
class MonthlyAmounts(Expression):
month: it.Date = None
amount: it.Float64 = None
@classmethod
def from_expression(cls, inputs: IbisTable[Transaction]):
cols = inputs.cols
table = (
inputs.table
@ it.Select(expr={"month": this[cols.date].truncate("M")})
@ it.Aggregate(by=["month"], sum=[cols.amount])
)
return cls.of(table)
Tip:
IbisSchemaclasses for yourExpressionoutputs can be generated automatically usingibis_typing.schema_writer. The code is backend-agnostic — schemas are derived from abstract Ibis table schemas, so no live backend is required.
3. Evaluate against a backend
from datetime import date
from ibis_typing import IbisConnection, evaluator
conn = IbisConnection()
transactions = Transaction.of_rows([Transaction(date=date(2024, 1, 15), amount=100.0, category="A")])
monthly_amounts = evaluator.from_expression(MonthlyAmounts, transactions)
results: list[MonthlyAmounts] = list(conn.fetch_table(monthly_amounts))
4. Test with Hypothesis
pytest fixtures are registered automatically — no conftest.py needed.
from hypothesis import given, strategies as st
from ibis_typing.hypothesis import strategy_for
@given(st.lists(strategy_for(Transaction), min_size=1))
def test_monthly_amounts(evaluate_table, transactions):
actual, expected = evaluate_table(MonthlyAmounts, transactions)
assert sorted(actual) == sorted(expected)
Core concepts
graph TD
IbisSchema -->|describes| IbisTable
IbisTable -->|In| Expression
Expression -->|Out| IbisTable
TableProvider -->|provides tables to| Expression
IbisConnection -->|fetches typed rows from| IbisTable
ChecksumBuckets -->|incremental inputs to| IncrementalExpression
IncrementalExpression -->|is a| Expression
BucketedInputsExpression -->|is a| IncrementalExpression
RevertibleTableExpression -->|can revert| Expression
| Class | Purpose |
|---|---|
IbisSchema |
Base class for typed table schemas (attrs frozen dataclass) |
IbisTable[S] |
Generic typed wrapper around ibis.Table |
Expression |
Abstract base for typed ibis transforms |
IbisConnection |
Typed backend wrapper: fetch_table(), evaluate(), read/write_parquet() |
BucketedInputsExpression |
Expression that only re-runs for changed input buckets |
ChecksumBuckets |
Checksum-based incremental input tracking |
RevertibleTableExpression |
Transform that can undo itself back to the original schema |
Type aliases
Declare schema fields using column-type aliases from ibis_typing.it:
from ibis_typing import it
it.Int8, it.Int16, it.Int32, it.Int64
it.Float32, it.Float64
it.Boolean
it.String, it.Binary
it.Decimal
it.Date, it.Time, it.Timestamp
it.UUID, it.JSON
it.Array[it.Int64]
it.Map[it.String, it.Float64]
it.Struct[MyTypedDict]
Table operations
Use the infix @ operator for composable, typed table transforms:
from ibis_typing import IbisSchema, IbisTable, this, it
@frozen
class InputSchema(IbisSchema):
a: it.Float64 = None
b: it.Float64 = None
category: it.String = None
amount: it.Float64 = None
key: it.String = None
inputs: IbisTable[InputSchema] = ...
other_table: IbisTable = ...
cols = InputSchema.cols
table = InputSchema.of(
inputs.table
@ it.Select(cols.a, cols.b, expr={"c": this[cols.a] + this[cols.b]})
@ it.Aggregate(by=[cols.category], sum=[cols.amount])
@ it.InnerJoin(other_table.table, keys=[cols.key])
)
Pytest fixtures
The following fixtures are auto-registered via the pytest plugin entry point (no conftest.py needed):
| Fixture | Purpose |
|---|---|
evaluate_table |
Runs an Expression, returns (actual, expected) row lists |
fetch_table |
Fetches rows from an IbisTable via DuckDB |
ibis_connection |
Provides a DuckDB-backed IbisConnection |
Extras
ibis_typing.type_patch— patches installed ibis with typed@overloadstubs foribis.ifelse,ibis.cases,ibis.coalesce, etc.ibis_typing.schema_writer— code-gen: writeIbisSchema.pyfiles fromExpressionoutput schemasibis_typing.plot— plots the dependency graph of anExpressionusing matplotlib/graphvizibis_typing.custom— custom ibis operations:DateAddMonth,DateAddDay,ColumnChecksum,JsonParse,JsonFormat,UUIDFromInt,LuhnCheck
Contributing
git clone https://github.com/FortnoxAB/ibis-typing
cd ibis-typing
uv sync --all-extras
uv run python -m ibis_typing.type_patch
make test
Pull requests welcome. Please run make before submitting.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ibis_typing-1.0.0.tar.gz.
File metadata
- Download URL: ibis_typing-1.0.0.tar.gz
- Upload date:
- Size: 54.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bc10fbdc7a83e6f8eced0c8e0d9ea8f38976706d027a2c99f955d117c1c4fbd
|
|
| MD5 |
a26766fb6764166752d578029cf6e3f8
|
|
| BLAKE2b-256 |
1e219ce565263d8037848a226ff8b2c6227990869317b3ebbb5cf5fdb07c001c
|
File details
Details for the file ibis_typing-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ibis_typing-1.0.0-py3-none-any.whl
- Upload date:
- Size: 81.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d4016f96aee5d90fa5ec7d6914b66e51c4bbebcb4b1ff5c0dce3d4196c30d4c
|
|
| MD5 |
baa8b9bc352ef22f71e34883aca4148d
|
|
| BLAKE2b-256 |
e0324c5cabbc809d34a82efb96d151792d5766676e388312920fd8d2b7af3dea
|