Substrait
A Python package for Substrait, the cross-language specification for data compute operations.
Installation
You can install the Python substrait bindings from PyPI or conda-forge
pip install substrait
conda install -c conda-forge python-substrait # or use mamba
Goals
This project aims to provide a Python interface for the Substrait specification. It will allow users to construct and manipulate a Substrait Plan from Python for evaluation by a Substrait consumer, such as DataFusion or DuckDB.
Non-goals
This project is not an execution engine for Substrait Plans.
Status
This is an experimental package that is still under development.
Substrait version
The specification version a release targets is set by the substrait-protobuf,
substrait-extensions and substrait-antlr requirements in
pyproject.toml — the proto bindings, the standard extension
definitions and the grammar all come from those packages rather than being vendored
here. substrait.version.substrait_version reports it at runtime.
Building plans with the DataFrame API
The substrait.dataframe module is an ergonomic, fluent API for authoring
Substrait plans — a Polars/PySpark-style DataFrame with operator-overloaded
expressions, on top of the lower-level builders. It is the recommended way to
build plans by hand:
import substrait.dataframe as sub
plan = (
sub.read_named_table("people", {"id": sub.i64, "age": sub.i64, "name": sub.string})
.filter(sub.col("age") > 25)
.with_columns(adult=sub.col("age") >= 18)
.select("id", "name", "adult")
.to_plan()
)
plan is a substrait.proto.Plan ready to hand to a consumer such as DuckDB or
DataFusion. Install the extensions extra so function overloads resolve against
the standard Substrait extensions:
pip install "substrait[extensions]"
Example (low-level API)
The examples below construct plans with the raw substrait.proto and
substrait.builders layers. For most hand-authored plans, prefer the
DataFrame API above.
Produce a Substrait Plan
The substrait.proto module provides access to the classes
that represent a substrait Plan, thus allowing to create new plans.
Here is an example plan equivalent to SELECT first_name FROM person
where people table has first_name and surname columns of type String
>>> from substrait import proto
>>> plan = proto.Plan(
... relations=[
... proto.PlanRel(
... root=proto.RelRoot(
... names=["first_name"],
... input=proto.Rel(
... read=proto.ReadRel(
... named_table=proto.ReadRel.NamedTable(names=["people"]),
... base_schema=proto.NamedStruct(
... names=["first_name", "surname"],
... struct=proto.Type.Struct(
... types=[
... proto.Type(string=proto.Type.String(nullability=proto.Type.Nullability.NULLABILITY_REQUIRED)),
... proto.Type(string=proto.Type.String(nullability=proto.Type.Nullability.NULLABILITY_REQUIRED))
... ] # /types
... ) # /struct
... ) # /base_schema
... ) # /read
... ) # /input
... ) # /root
... ) # /PlanRel
... ] # /relations
... )
>>> print(plan)
relations {
root {
input {
read {
base_schema {
names: "first_name"
names: "surname"
struct {
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
}
}
named_table {
names: "people"
}
}
}
names: "first_name"
}
}
>>> serialized_plan = plan.SerializeToString()
>>> serialized_plan
b'\x1aA\x12?\n1\n/\x12#\n\nfirst_name\n\x07surname\x12\x0c\n\x04b\x02\x10\x02\n\x04b\x02\x10\x02:\x08\n\x06people\x12\nfirst_name'
Consume the Substrait Plan
The same plan we generated in the previous example,
can be loaded back from its binary representation
using the Plan.ParseFromString method:
>>> from substrait.proto import Plan
>>> p = Plan()
>>> p.ParseFromString(serialized_plan)
67
>>> p
relations {
root {
input {
read {
base_schema {
names: "first_name"
names: "surname"
struct {
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
}
}
named_table {
names: "people"
}
}
}
names: "first_name"
}
}
Load a Substrait Plan from JSON
A substrait plan can be loaded from the JSON representation
of the protobuf message using the protobuf python library:
>>> from substrait.proto import Plan
>>> from google.protobuf import json_format
>>> jsontext = """{
... "relations":[
... {
... "root":{
... "input":{
... "read":{
... "baseSchema":{
... "names":[
... "first_name",
... "surname"
... ],
... "struct":{
... "types":[
... {
... "string":{
... "nullability":"NULLABILITY_REQUIRED"
... }
... },
... {
... "string":{
... "nullability":"NULLABILITY_REQUIRED"
... }
... }
... ]
... }
... },
... "namedTable":{
... "names":[
... "people"
... ]
... }
... }
... },
... "names":[
... "first_name"
... ]
... }
... }
... ]
... }"""
>>> json_format.Parse(text=jsontext, message=Plan())
relations {
root {
input {
read {
base_schema {
names: "first_name"
names: "surname"
struct {
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
types {
string {
nullability: NULLABILITY_REQUIRED
}
}
}
}
named_table {
names: "people"
}
}
}
names: "first_name"
}
}
Produce a Substrait Plan with Ibis
Let's use an existing Substrait producer, Ibis, to provide an example using Python Substrait as the consumer.
In [1]: import ibis
In [2]: movie_ratings = ibis.table(
...: [
...: ("tconst", "str"),
...: ("averageRating", "str"),
...: ("numVotes", "str"),
...: ],
...: name="ratings",
...: )
...:
In [3]: query = movie_ratings.select(
...: movie_ratings.tconst,
...: avg_rating=movie_ratings.averageRating.cast("float"),
...: num_votes=movie_ratings.numVotes.cast("int"),
...: )
In [4]: from ibis_substrait.compiler.core import SubstraitCompiler
In [5]: compiler = SubstraitCompiler()
In [6]: protobuf_msg = compiler.compile(query).SerializeToString()
In [7]: from substrait.proto import Plan
In [8]: my_plan = Plan()
In [9]: my_plan.ParseFromString(protobuf_msg)
Out[9]: 186
In [10]: print(my_plan)
relations {
root {
input {
project {
common {
emit {
output_mapping: 3
output_mapping: 4
output_mapping: 5
}
}
input {
read {
common {
direct {
}
}
base_schema {
names: "tconst"
names: "averageRating"
names: "numVotes"
struct {
types {
string {
nullability: NULLABILITY_NULLABLE
}
}
types {
string {
nullability: NULLABILITY_NULLABLE
}
}
types {
string {
nullability: NULLABILITY_NULLABLE
}
}
nullability: NULLABILITY_REQUIRED
}
}
named_table {
names: "ratings"
}
}
}
expressions {
selection {
direct_reference {
struct_field {
}
}
root_reference {
}
}
}
expressions {
cast {
type {
fp64 {
nullability: NULLABILITY_NULLABLE
}
}
input {
selection {
direct_reference {
struct_field {
field: 1
}
}
root_reference {
}
}
}
failure_behavior: FAILURE_BEHAVIOR_THROW_EXCEPTION
}
}
expressions {
cast {
type {
i64 {
nullability: NULLABILITY_NULLABLE
}
}
input {
selection {
direct_reference {
struct_field {
field: 2
}
}
root_reference {
}
}
}
failure_behavior: FAILURE_BEHAVIOR_THROW_EXCEPTION
}
}
}
}
names: "tconst"
names: "avg_rating"
names: "num_votes"
}
}
version {
minor_number: 24
producer: "ibis-substrait"
}
Contributing
See CONTRIBUTING.md for how the specification relates to this repository, the development environment, the lint / format / test commands, and the commit and pull request conventions. Releases are automated — RELEASING.md describes that process.
Getting Involved
To learn more, head over to Substrait, our parent project, and join our community.
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 substrait-0.31.0.tar.gz.
File metadata
- Download URL: substrait-0.31.0.tar.gz
- Upload date:
- Size: 259.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe594cbd5640e77dff5475901cdbdf211b8d76ce13ea86eaeb7d45d47b6a795a
|
|
| MD5 |
77889e68c2fa95b427b58a4fdff975a0
|
|
| BLAKE2b-256 |
8e0ad6743f631fdd6dba4b0f77bf17920210097968f9dc196ccb032d92e89b8f
|
Provenance
The following attestation bundles were made for substrait-0.31.0.tar.gz:
Publisher:
release.yml on substrait-io/substrait-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
substrait-0.31.0.tar.gz -
Subject digest:
fe594cbd5640e77dff5475901cdbdf211b8d76ce13ea86eaeb7d45d47b6a795a - Sigstore transparency entry: 2483476465
- Sigstore integration time:
-
Permalink:
substrait-io/substrait-python@4c0aebbbdf059ccb2685c5042e416cc1d2e6f05d -
Branch / Tag:
refs/tags/v0.31.0 - Owner: https://github.com/substrait-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4c0aebbbdf059ccb2685c5042e416cc1d2e6f05d -
Trigger Event:
push
-
Statement type:
File details
Details for the file substrait-0.31.0-py3-none-any.whl.
File metadata
- Download URL: substrait-0.31.0-py3-none-any.whl
- Upload date:
- Size: 104.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
302c5a262738c4b9bcea681fe577b7b9b7ddbeb541239495d9b96ba0464f5017
|
|
| MD5 |
7369eab013b0299b24be5bc9a3b7ae64
|
|
| BLAKE2b-256 |
57e9622c94643324b7fd72bfc86ea29d72fb990c9ee4db829296400529b1cfff
|
Provenance
The following attestation bundles were made for substrait-0.31.0-py3-none-any.whl:
Publisher:
release.yml on substrait-io/substrait-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
substrait-0.31.0-py3-none-any.whl -
Subject digest:
302c5a262738c4b9bcea681fe577b7b9b7ddbeb541239495d9b96ba0464f5017 - Sigstore transparency entry: 2483476489
- Sigstore integration time:
-
Permalink:
substrait-io/substrait-python@4c0aebbbdf059ccb2685c5042e416cc1d2e6f05d -
Branch / Tag:
refs/tags/v0.31.0 - Owner: https://github.com/substrait-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4c0aebbbdf059ccb2685c5042e416cc1d2e6f05d -
Trigger Event:
push
-
Statement type: