FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

ENH: Store polars DataFrames as Parquet and support `pl.LazyFrame` inputs by basnijholt · Pull Request #966 · pipefunc/pipefunc · GitHub

ENH: Store polars DataFrames as Parquet and support pl.LazyFrame inputs - #966

Merged
basnijholt merged 6 commits into
mainfrom
polars
Jun 10, 2026
Merged

ENH: Store polars DataFrames as Parquet and support pl.LazyFrame inputs#966
basnijholt merged 6 commits into
mainfrom
polars

Conversation

basnijholt commented Jun 10, 2026
edited
Loading

Copy link
Copy Markdown
Collaborator

Summary

Implements the remaining part of #879 (see this comment): Parquet on-disk serialization for polars.DataFrame outputs and lazy (pl.LazyFrame) loading in downstream functions.

  • Parquet storage: dump() writes top-level pl.DataFrame objects as Parquet files (same path/filename — load() detects the format via the PAR1 magic bytes, so resume/existence logic is untouched). Falls back to cloudpickle if Parquet serialization fails. Applies to file_array storage, single outputs in a run_folder, and anything else using dump/load.
  • Lazy inputs: parameters annotated as pl.LazyFrame receive lazy frames. When the upstream output is a non-mapspec-indexed Parquet file on disk, the function gets a true pl.scan_parquet(...) — the DataFrame is never materialized and polars can apply predicate/projection pushdown. In other paths (mapspec elements, storage="dict", Pipeline.run, user-passed inputs) the DataFrame is converted with .lazy().
  • Type validation: pl.DataFrame output → pl.LazyFrame input edges pass validate_type_annotations (one direction only).
  • Caching: to_hashable now handles pl.LazyFrame by hashing the serialized query plan.
  • Docs section in Function Inputs and Outputs + FAQ pointer.

Deliberately out of scope: sinking pl.LazyFrame outputs to Parquet (load() couldn't distinguish them from DataFrame outputs), lazy scanning of individual mapspec elements, and zarr storage (keeps its pickle codec).

Testing

  • 15 new tests in tests/test_polars_parquet.py: dump/load roundtrips, pickle fallback (monkeypatched failure), FileArray element/to_array paths, scan-vs-.lazy() behavior in map (file/dict storage), element-wise mapspec with parallel=True (LazyFrames pickle across processes), reductions still receiving DataFrames, Pipeline.run, type compatibility, and to_hashable.
  • Full test suite passes locally (1426 passed); pre-commit (ruff + mypy) clean.

Closes #879

Implements the remaining part of #879: DataFrame outputs are serialized
as Parquet files on disk (with cloudpickle fallback), and parameters
annotated as pl.LazyFrame receive lazy frames - a true pl.scan_parquet
when the upstream output is stored as Parquet, otherwise .lazy().
Comment thread pipefunc/typing.py

import numpy as np

from pipefunc._utils import is_imported

codecov Bot commented Jun 10, 2026
edited
Loading

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
pipefunc/_pipefunc.py 100.00% <100.00%> (ø)
pipefunc/_pipeline/_base.py 100.00% <100.00%> (ø)
pipefunc/_utils.py 100.00% <100.00%> (ø)
pipefunc/cache.py 100.00% <100.00%> (ø)
pipefunc/map/_run.py 100.00% <100.00%> (ø)
pipefunc/map/_storage_array/_file.py 100.00% <100.00%> (ø)
pipefunc/typing.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Avoids iterating all parameter annotations per map element in
_convert_lazyframe_kwargs; the no-polars-params fast path is now a
single cached attribute lookup (~46 ns/call).

codspeed-hq Bot commented Jun 10, 2026
edited
Loading

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing polars (7abd39f) with main (2eae216)

…conversion onto PipeFunc

- load() previously opened every file twice (magic-byte sniff + read);
  now peeks and rewinds a single handle. This path is hot for all users.
- _convert_lazyframe_kwargs is now a PipeFunc method, living next to the
  _lazyframe_parameters cache it uses, so _pipeline/_base.py no longer
  imports a private helper from map/_run.py.
- Use bytes.startswith for the magic check in _load_all.
The all-deps CI session imports polars at collection time, so the
is_imported('polars') early returns never executed there (codecov/patch
flagged them). Simulate via monkeypatch.delitem(sys.modules, 'polars').
…itive

CodeQL mis-resolves the stdlib 'typing' import inside _utils.py as
pipefunc.typing (the package has a module of that name), so importing
is_imported from _utils here looked like a cyclic import. Use the
sys.modules check directly, as cache.py already does.

Copy link
Copy Markdown
Contributor

✅ PR Title Formatted Correctly

The title of this PR has been updated to match the correct format. Thank you!

basnijholt merged commit a503d8e into main Jun 10, 2026
16 of 17 checks passed
basnijholt deleted the polars branch June 10, 2026 17:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

First-class polars.DataFrame support

2 participants


Back | FazBrowse Home | New Git URL