Skip to content

API (PlanFrame core)

This is a light API reference intended for quick discovery. For the full surface area, see the generated reference for each module.

Optional API skins

Typed mixins (import from planframe.spark / planframe.pandas, or from planframe import spark / pandas) layer familiar naming on Frame without adding backend dependencies. Guides: PySpark-like API, pandas-like API.

planframe.spark.frame.SparkFrame

Bases: Frame[SchemaT, BackendFrameT, BackendExprT], Generic[SchemaT, BackendFrameT, BackendExprT]

Mixin-style subclass adding PySpark naming; combine with a concrete backend frame.

Example::

from planframe.spark import SparkFrame


class Users(PolarsFrame, SparkFrame):
    id: int

union(other)

PySpark union preserves duplicates (SQL UNION ALL).

planframe.pandas.frame.PandasLikeFrame

Bases: Frame[SchemaT, BackendFrameT, BackendExprT], Generic[SchemaT, BackendFrameT, BackendExprT]

Mixin-style pandas-flavored API; combine with a concrete backend frame.

assign(**columns)

Pandas-like assign, lowered to repeated with_column.

filter(*predicates, items=None, like=None, regex=None)

Row filter (PlanFrame) or column filter (pandas-style), depending on arguments.

  • df.filter(predicate) behaves like PlanFrame Frame.filter.
  • df.filter(items=...|like=...|regex=...) behaves like pandas column selection.

groupby(by, *, sort=False, dropna=True)

Pandas-like groupby, lowered to core group_by.

Note: PlanFrame does not implement pandas index semantics; this returns a PlanFrame grouped object with .agg(...).

melt(*, id_vars, value_vars, var_name='variable', value_name='value')

Pandas-like melt, lowered to core unpivot.

query(expr)

query(expr: str) -> PandasLikeFrame[Any, Any, Any]
query(
    expr: Series[bool] | Expr[bool],
) -> PandasLikeFrame[Any, Any, Any]

Pandas-like query.

Supported forms:

  • Typed: Series[bool] / Expr[bool]
  • String (tiny subset): col op literal (no boolean ops, no functions, no parens)

rename_pandas(*, columns=None, errors='raise')

Pandas-style rename(columns=..., errors=...).

planframe.frame.Frame

Bases: Generic[SchemaT, BackendFrameT, BackendExprT]

source(data, *, adapter, schema) classmethod

schema()

plan()

optimize(*, level=...)

collect(*, name=..., options=...)

acollect(*, name=..., options=...) async

to_dicts(*, options=...)

ato_dicts(*, options=...) async

to_dict(*, options=...)

ato_dict(*, options=...) async

planframe.execution_options.ExecutionOptions

Backend-agnostic execution-time hints.

These options are only consulted at execution/materialization boundaries (e.g. collect, to_dicts, to_dict) and must not affect schema evolution.

planframe.plan.join_options.JoinOptions

Optional join hints for backends that support them (for example Polars).

Fields that are None are omitted when calling the backend so its defaults apply.

streaming / engine_streaming mirror :class:planframe.execution_options.ExecutionOptions (user-level streaming vs engine-level streaming); backends may support none, one, or both.

Polars (planframe-polars) mapping and precedence

Optional fields are forwarded to :meth:polars.LazyFrame.join when the installed Polars version supports the corresponding keyword. Applied in this order; later steps override earlier ones where both set the same underlying argument:

. coalesce, validate, join_nulls (as nulls_equal), maintain_order.

. streaming — sets allow_parallel = not streaming (disable parallel join paths

when the user prefers streaming-style execution).

. allow_parallel — overwrites allow_parallel from the previous step.

. force_parallel — sets Polars force_parallel (separate from allow_parallel).

. engine_streaming — set when supported by Polars (not all versions expose it).

planframe.execution.execute_plan

Execute a :class:planframe.plan.nodes.PlanNode tree.

This is the supported public plan interpreter used by :meth:planframe.frame.Frame.collect.

Important: - This returns the backend frame after applying the plan, but it does not call :meth:planframe.backend.adapter.BaseAdapter.collect unless collect=True is provided.

planframe.execution.execute_plan_async

Async counterpart of :func:execute_plan.

Runs the synchronous plan interpreter in a worker thread (:func:asyncio.to_thread) so callers can await plan execution without blocking the event loop. Transform interpretation remains synchronous; pair with :meth:~planframe.backend.adapter.BaseAdapter.acollect (or other async materializers) when the backend supports async I/O.

See also :meth:planframe.frame.Frame.acollect_backend / :meth:~planframe.frame.Frame.collect_async.

See also: Async execution contract and Thread-safety expectations in the Creating an adapter guide (docs/planframe/guides/creating-an-adapter.md; ReadTheDocs path planframe/guides/creating-an-adapter/).

planframe.materialize

Adapter-friendly materialization boundaries (columnar export + optional factory).

These mirror :meth:planframe.frame.Frame.to_dict / :meth:~planframe.frame.Frame.ato_dict with the same :class:~planframe.execution_options.ExecutionOptions contract — use them when you want a stable import (from planframe.materialize import ...) instead of calling Frame methods directly. See the Creating an adapter — Columnar boundary <https://planframe.readthedocs.io/en/latest/planframe/guides/creating-an-adapter/#columnar-boundary-materialize>__ section.

For chunked columnar export (optional adapter protocol, not wired here yet), see the Columnar streaming design note in the PlanFrame docs and AdapterColumnarStreamer in planframe.backend.io.

PlanFrame does not construct Pydantic/dataclass models here; supply a factory when needed.

amaterialize_columns(frame, *, options=None) async

Async columnar materialization (same as :meth:~planframe.frame.Frame.ato_dict).

amaterialize_into(frame, factory, *, options=None) async

Like :func:materialize_into, using the async to_dict path.

materialize_columns(frame, *, options=None)

Return columnar data for frame (dict[column_name, column_values]).

Thin wrapper around :meth:planframe.frame.Frame.to_dictoptions are forwarded unchanged.

materialize_into(frame, factory, *, options=None)

Materialize columns, then pass them to factory.

factory can wrap Pydantic models, dataclasses, Arrow tables, or any custom type; PlanFrame stays agnostic to the output shape.

planframe.backend.io.AdapterColumnarStreamer

Optional protocol (spike) for chunked columnar batches — see Columnar streaming (design).

Bases: Protocol[BackendFrameT]

Optional adapter surface for chunked columnar export (design spike for 1.3+).

Each chunk is a columnar mapping dict[column_name, list[values]] where every value list has the same length (rows in that chunk). Chunk boundaries are adapter-defined (e.g. engine batch size). Column names should be consistent across chunks for a given materialization.

This is not the same as :class:AdapterRowStreamer, which streams rows (dict[str, object] per row) and is integrated with Frame.stream_dicts / Frame.astream_dicts. Columnar chunking is for hosts that want to build Arrow tables, batched numpy/Pandas loads, etc., without holding a full dict[str, list[object]] in memory.

Integration status: PlanFrame core does not yet call this protocol from :func:planframe.materialize.materialize_columns or Frame.to_dict. Adapters may implement it so hosts can isinstance(adapter, AdapterColumnarStreamer) and call the iterators after collect / acollect, forwarding the same :class:~planframe.execution_options.ExecutionOptions you would pass to to_dict. Use streaming / engine_streaming hints the same way as for other materializers.

Contract: if you claim support, implement both :meth:iter_columnar_chunks and :meth:aiter_columnar_chunks (mirroring :class:AdapterRowStreamer).

See the PlanFrame design note Columnar streaming (docs/planframe/design/columnar-streaming.md).

planframe.compile_context.PlanCompileContext

Internal helper shared by Frame and execute_plan for compiling expression IR and related structures. Adapter authors rarely import it directly; see Core layout.

Bases: Generic[BackendFrameT, BackendExprT]

Holds (adapter, schema) and compiles expressions and join metadata once per context.

compile_project_items(items)

Lower :class:~planframe.plan.nodes.Project items to :class:CompiledProjectItem.

planframe.plan.walk.iter_plan_nodes

Iterate plan nodes in a deterministic pre-order traversal.

Pre-order means the current node is yielded before its children.

By default this walks only the linear prev chain (the primary pipeline). Nodes that reference other frames (Join.right, concat other) are treated as boundaries and are not descended into unless include_side_frames=True.

When include_side_frames=True, the traversal yields: - the current node - then its prev subtree (depth-first) - then any side frame subtrees (depth-first), in a stable order: - for Join: RHS (right) after the left chain - for concats: other after the left chain