Skip to content

Commit cbfdc4c

Browse files
committed
refactor(api): group ergonomic API under substrait.dataframe
Move the six new higher-level modules out of the shared substrait namespace root into a dedicated substrait.dataframe subpackage: api.py -> dataframe/__init__.py dtypes.py -> dataframe/dtypes.py expr.py -> dataframe/expr.py frame.py -> dataframe/frame.py functions.py -> dataframe/functions.py extension_relations.py -> dataframe/extension_relations.py substrait is a PEP 420 namespace package shared with substrait-protobuf / -antlr / -extensions, so scattering generic top-level names (expr, frame, functions, ...) at the namespace root risks colliding with sibling distributions. Grouping them under one owned subpackage gives a single import surface and matches the existing per-entry-point layout (builders/, sql/, narwhals/). The name is now consistent with those siblings -- named for what it is rather than the generic "api". Entry point becomes `import substrait.dataframe as sub`. Also renames tests/api -> tests/dataframe and examples/api_example -> dataframe_example, and updates docstring cross-references. Per the review discussion in PR #204.
1 parent 6bd9745 commit cbfdc4c

16 files changed

Lines changed: 57 additions & 49 deletions
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
"""Example usage of the ergonomic `substrait.api` facade.
1+
"""Example usage of the ergonomic `substrait.dataframe` facade.
22
33
Compare this with `builder_example.py`, which builds the same kinds of plans
44
with the lower-level `substrait.builders.*` functions.
55
"""
66

7-
import substrait.api as sub
7+
import substrait.dataframe as sub
88
from substrait.utils.display import pretty_print_plan
99

1010

examples/narwhals_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# construction through Narwhals (`nw.from_native`), so backend-agnostic Narwhals
33
# code compiles to a Substrait plan.
44
#
5-
# For building plans directly (without Narwhals), see `api_example.py`, which
6-
# uses the Substrait-native DataFrame in `substrait.api` / `substrait.frame`.
5+
# For building plans directly (without Narwhals), see `dataframe_example.py`,
6+
# which uses the Substrait-native DataFrame in `substrait.dataframe`.
77
#
88
# /// script
99
# dependencies = [
Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
A single, shallow import that gets you productive::
44
5-
import substrait.api as sub
5+
import substrait.dataframe as sub
66
77
plan = (
88
sub.read_named_table("people", {"id": sub.i64, "age": sub.i64, "name": sub.string})
@@ -12,10 +12,16 @@
1212
.to_plan()
1313
)
1414
15-
This lives as a *submodule* (``substrait.api``) rather than the package root on
16-
purpose: ``substrait`` is a PEP 420 namespace package shared with the
17-
``substrait-protobuf`` distribution, so adding ``substrait/__init__.py`` would
18-
shadow ``substrait.algebra_pb2`` and friends.
15+
This is the Substrait-*native* fluent DataFrame/Expr API -- the higher-level
16+
counterpart to the lower-level ``substrait.builders`` layer, and a sibling to
17+
the other entry points (``substrait.sql``, ``substrait.narwhals``). It lives in
18+
its own subpackage rather than at the ``substrait`` package root because
19+
``substrait`` is a PEP 420 namespace package shared with the
20+
``substrait-protobuf`` distribution: an ``substrait/__init__.py`` would shadow
21+
``substrait.algebra_pb2`` and friends, and scattering ``expr`` / ``frame`` /
22+
... at the shared namespace root would risk colliding with the sibling
23+
distributions. Grouping them under ``substrait.dataframe`` keeps a single,
24+
clearly owned import surface.
1925
2026
Everything here is an additive facade over the existing ``substrait.builders``,
2127
``substrait.extension_registry`` and ``substrait.proto`` layers, which remain
@@ -44,7 +50,7 @@
4450

4551
# Primitive / no-argument type shortcuts as nullability-aware DataType objects
4652
# (sub.i64 -> nullable; sub.i64.non_null -> required; sub.i64() still callable).
47-
from substrait.dtypes import (
53+
from substrait.dataframe.dtypes import (
4854
DataType,
4955
binary,
5056
boolean,
@@ -59,7 +65,7 @@
5965
string,
6066
uuid,
6167
)
62-
from substrait.expr import (
68+
from substrait.dataframe.expr import (
6369
Expr,
6470
all_,
6571
any_,
@@ -78,12 +84,12 @@
7884
when,
7985
)
8086
from substrait.extension_registry import ExtensionRegistry
81-
from substrait.extension_relations import (
87+
from substrait.dataframe.extension_relations import (
8288
ExtensionLeafDetail,
8389
ExtensionMultiDetail,
8490
ExtensionSingleDetail,
8591
)
86-
from substrait.frame import (
92+
from substrait.dataframe.frame import (
8793
DataFrame,
8894
create_table,
8995
create_view,
@@ -100,7 +106,7 @@
100106
read_parquet,
101107
update_table,
102108
)
103-
from substrait.functions import f, functions_for
109+
from substrait.dataframe.functions import f, functions_for
104110

105111
__all__ = [
106112
# entry points
File renamed without changes.
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
Daft's own native frame). It is a thin, chainable wrapper over the
66
``substrait.builders.plan`` functions: it carries an ``ExtensionRegistry`` so it
77
does not have to be threaded through every call, and it takes
8-
:class:`~substrait.expr.Expr` objects (or bare column names / Python scalars)
9-
rather than raw ``scalar_function`` invocations::
8+
:class:`~substrait.dataframe.expr.Expr` objects (or bare column names / Python
9+
scalars) rather than raw ``scalar_function`` invocations::
1010
11-
import substrait.api as sub
11+
import substrait.dataframe as sub
1212
1313
plan = (
1414
sub.read_named_table("people", {"id": sub.i64, "age": sub.i64})
@@ -38,7 +38,7 @@
3838

3939
from substrait.builders import plan as _plan
4040
from substrait.builders import type as _type
41-
from substrait.expr import Expr, Measure, col, lit
41+
from substrait.dataframe.expr import Expr, Measure, col, lit
4242
from substrait.extension_registry import ExtensionRegistry
4343
from substrait.type_inference import infer_plan_schema, reference_subtrees
4444

@@ -204,7 +204,7 @@ def f(self):
204204
"""
205205
cached = getattr(self, "_functions_ns", None)
206206
if cached is None:
207-
from substrait.functions import functions_for
207+
from substrait.dataframe.functions import functions_for
208208

209209
cached = functions_for(self._registry)
210210
self._functions_ns = cached
@@ -485,7 +485,7 @@ def extension(self, detail: Any) -> "DataFrame":
485485
"""Apply a custom single-input relation (ExtensionSingleRel).
486486
487487
``detail`` is an
488-
:class:`~substrait.extension_relations.ExtensionSingleDetail` (its
488+
:class:`~substrait.dataframe.extension_relations.ExtensionSingleDetail` (its
489489
``derive_schema`` defines the output) or a raw ``google.protobuf.Any``
490490
(the input schema is then assumed to pass through). Register the detail
491491
class via ``ExtensionRegistry.register_extension_relation`` for schema
@@ -498,7 +498,7 @@ def extension_multi(
498498
) -> "DataFrame":
499499
"""A custom multi-input relation (ExtensionMultiRel) over this frame and
500500
``others``; ``detail`` is an
501-
:class:`~substrait.extension_relations.ExtensionMultiDetail`."""
501+
:class:`~substrait.dataframe.extension_relations.ExtensionMultiDetail`."""
502502
inputs = [self._plan, *(o._plan for o in others)]
503503
return self._next(_plan.extension_multi(inputs, detail))
504504

@@ -754,7 +754,7 @@ def extension_leaf(
754754
"""Start a DataFrame from a custom leaf relation (ExtensionLeafRel).
755755
756756
``detail`` is an
757-
:class:`~substrait.extension_relations.ExtensionLeafDetail`; its
757+
:class:`~substrait.dataframe.extension_relations.ExtensionLeafDetail`; its
758758
``derive_schema`` defines the source's output columns.
759759
"""
760760
return DataFrame(_plan.extension_leaf(detail), registry)
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
extensions -- scalar, aggregate and window -- so anything the specification
55
ships is reachable by name and hides the extension-URN / signature plumbing::
66
7-
import substrait.api as sub
7+
import substrait.dataframe as sub
88
99
sub.f.sum(sub.col("amount"))
1010
sub.f.substring(sub.col("name"), 1, 3)
1111
sub.f.coalesce(sub.col("a"), sub.col("b"))
1212
sub.f.row_number()
1313
14-
Each helper returns an :class:`~substrait.expr.Expr`. The namespace is built
15-
lazily on first attribute access from :func:`substrait.frame.default_registry`,
14+
Each helper returns an :class:`~substrait.dataframe.expr.Expr`. The namespace is
15+
built lazily on first attribute access from
16+
:func:`substrait.dataframe.frame.default_registry`,
1617
and supports ``dir(sub.f)`` for discovery/tab-completion.
1718
1819
Some function names appear in more than one extension (e.g. ``add`` in
@@ -40,7 +41,7 @@
4041
scalar_function,
4142
window_function,
4243
)
43-
from substrait.expr import Expr
44+
from substrait.dataframe.expr import Expr
4445
from substrait.extension_registry.function_entry import FunctionType
4546
from substrait.type_inference import infer_extended_expression_schema
4647

@@ -141,7 +142,7 @@ def _ensure(self) -> dict:
141142
if self._fns is None:
142143
registry = self._registry
143144
if registry is None:
144-
from substrait.frame import default_registry
145+
from substrait.dataframe.frame import default_registry
145146

146147
registry = default_registry()
147148
object.__setattr__(self, "_fns", _build_functions(registry))

src/substrait/extension_registry/registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def register_extension_relation(self, detail_cls) -> None:
5353
Enables schema inference for ``ExtensionLeaf/Single/MultiRel`` built with
5454
an instance of ``detail_cls``: inference reconstructs the detail from the
5555
plan's ``Any`` and calls its ``derive_schema``. See
56-
:mod:`substrait.extension_relations`. Registration is process-global
56+
:mod:`substrait.dataframe.extension_relations`. Registration is process-global
5757
(type_urls are globally unique), so inference works on any plan.
5858
"""
5959
from substrait.type_inference import register_extension_relation

src/substrait/narwhals/dataframe.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
hooks (``__narwhals_lazyframe__`` / ``__narwhals_namespace__``) and translating
66
Narwhals calls into Substrait plan builders.
77
8-
It is distinct from :mod:`substrait.frame`, which is the Substrait-*native*
9-
fluent DataFrame you can call directly without Narwhals. This layer sits on top
10-
of that native machinery; the two compose rather than compete.
8+
It is distinct from :mod:`substrait.dataframe.frame`, which is the
9+
Substrait-*native* fluent DataFrame you can call directly without Narwhals. This
10+
layer sits on top of that native machinery; the two compose rather than compete.
1111
1212
Status: experimental / minimal -- it currently implements only a subset of the
13-
Narwhals compliant protocol, to be built out on top of :mod:`substrait.frame`.
13+
Narwhals compliant protocol, to be built out on top of
14+
:mod:`substrait.dataframe.frame`.
1415
"""
1516

1617
from typing import Iterable, Union
@@ -24,7 +25,7 @@ class DataFrame:
2425
"""Narwhals-compliant wrapper around a Substrait plan.
2526
2627
Presents as a Narwhals ``LazyFrame`` backend. For direct, non-Narwhals plan
27-
building use :class:`substrait.frame.DataFrame` instead.
28+
building use :class:`substrait.dataframe.frame.DataFrame` instead.
2829
"""
2930

3031
def __init__(self, plan):

0 commit comments

Comments
 (0)