Skip to content

Commit 31adfdd

Browse files
ptomecekCopilot
andcommitted
Rename multi_source to pushdown_combine
The name `multi_source` described the inputs (multiple sources) rather than what the function does. Its defining feature is coordinated predicate pushdown into a user-supplied `combine` function; the multi-source aspect is incidental. Rename to `pushdown_combine` to name the behavior, consistent with polars/polars-io-tools naming. - Move io_sources/multi_source.py -> pushdown_combine.py - Move tests/io_sources/test_multi_source.py -> test_pushdown_combine.py - Update the function, __all__, imports, test class names, and docs - FilterSpec is unchanged Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Pascal Tomecek <40371786+ptomecek@users.noreply.github.com>
1 parent b1edb6e commit 31adfdd

13 files changed

Lines changed: 142 additions & 142 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ For a guided walkthrough, start with the
7979
Delta Lake or ClickHouse, including streaming/chunked writes and transparent handling
8080
of types the target store cannot represent natively.
8181
- **Pushdown-preserving query building**`filtered_join`, `filtered_join_asof`,
82-
`join_between`, `multi_source`, `concat_named`, and `ts_with_columns` express joins,
82+
`join_between`, `pushdown_combine`, `concat_named`, and `ts_with_columns` express joins,
8383
multi-source composition, and rolling/lookback time-series logic without blocking the
8484
filter pushdown that those operations normally defeat.
8585
- **Caching**`cache` keeps an in-memory, column- and partition-level cache for

docs/wiki/API-Reference.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# API reference
22

33
The public surface of `polars-io-tools`. Importing the package (`import polars_io_tools`) registers the `piot` namespace and re-exports the functions below at
4-
the top level (`from polars_io_tools import scan_db, multi_source, ...`).
4+
the top level (`from polars_io_tools import scan_db, pushdown_combine, ...`).
55

66
Most operations are available two ways:
77

@@ -224,10 +224,10 @@ Top-level form of `lf.piot.sink_clickhouse`; see the namespace entry above.
224224

225225
## Composing frames
226226

227-
### `multi_source`
227+
### `pushdown_combine`
228228

229229
```python
230-
multi_source(sources, combine, *, combine_kwargs=None, sources_as_kwargs=False,
230+
pushdown_combine(sources, combine, *, combine_kwargs=None, sources_as_kwargs=False,
231231
log_explain=False) -> pl.LazyFrame
232232
```
233233

docs/wiki/Concepts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ it cannot see in advance. Three common cases motivate most of this library:
7676

7777
In each case the trick is the same: intercept the predicate at a custom source, transform
7878
it into something that is safe to apply earlier, and re-apply the exact original filter at
79-
the end so the result is identical to the naive version. [`multi_source`](Query-Optimization#combine-sources-with-coordinated-filter-pushdown)
79+
the end so the result is identical to the naive version. [`pushdown_combine`](Query-Optimization#combine-sources-with-coordinated-filter-pushdown)
8080
generalises this to arbitrary compositions, with a per-source `FilterSpec` describing how
8181
each output filter maps onto each source.
8282

docs/wiki/Query-Optimization.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,16 @@ For overlapping intervals where each match should produce a row, use Polars'
7979

8080
## Combine sources with coordinated filter pushdown
8181

82-
`multi_source` builds a single `LazyFrame` from several named sources plus a `combine`
82+
`pushdown_combine` builds a single `LazyFrame` from several named sources plus a `combine`
8383
function. When the result is filtered, each source receives a *transformed* version of
8484
the filter described by its `FilterSpec` — a renamed column, an expanded date range, or
8585
a remapped value — and the original filter is re-applied after `combine` runs.
8686

8787
```python
8888
from datetime import timedelta
89-
from polars_io_tools import multi_source, FilterSpec
89+
from polars_io_tools import pushdown_combine, FilterSpec
9090

91-
lf = multi_source(
91+
lf = pushdown_combine(
9292
sources={
9393
"prices": (prices_lf, {"date": FilterSpec(), "id": FilterSpec()}),
9494
"signals": (signals_lf, {

polars_io_tools/io_sources/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .lazy_iter_rows import *
1818
from .lazy_narwhals_reader import *
1919
from .lazy_sql_reader import *
20-
from .multi_source import *
20+
from .pushdown_combine import *
2121
from .sql_dialects import *
2222
from .translated_source import *
2323
from .ts import *
@@ -33,7 +33,7 @@
3333
from .lazy_clickhouse_reader import scan_clickhouse
3434
from .lazy_clickhouse_writer import sink_clickhouse # noqa: TC004
3535
from .lazy_iter_rows import iter_rows # noqa: TC004
36-
from .multi_source import FilterSpec, multi_source
36+
from .pushdown_combine import FilterSpec, pushdown_combine
3737
from .ts import ts_with_columns # noqa: TC004
3838
from .util import filter_no_pushdown, with_columns_topo # noqa: TC004
3939

polars_io_tools/io_sources/multi_source.py renamed to polars_io_tools/io_sources/pushdown_combine.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""
2-
Multi-source LazyFrame composition with coordinated filter pushdown.
2+
Coordinated filter pushdown across a user-defined combine of multiple sources.
33
4-
This module provides the ``multi_source`` function for creating LazyFrames that
4+
This module provides the ``pushdown_combine`` function for creating LazyFrames that
55
combine multiple data sources while automatically propagating and transforming
66
filters to each source appropriately.
77
88
Example usage::
99
1010
import polars_io_tools as cpl
11-
from polars_io_tools import multi_source, FilterSpec
11+
from polars_io_tools import pushdown_combine, FilterSpec
1212
13-
lf = multi_source(
13+
lf = pushdown_combine(
1414
sources={
1515
"left": (left_lf, {
1616
"date": FilterSpec(),
@@ -52,7 +52,7 @@
5252
from .set_visitor import convert_expr_to_valid_values
5353
from .util import collect_lf_in_io_source, register_io_source_with_is_pure
5454

55-
__all__ = ("FilterSpec", "multi_source")
55+
__all__ = ("FilterSpec", "pushdown_combine")
5656

5757
log = logging.getLogger(__name__)
5858

@@ -204,7 +204,7 @@ def _is_temporal_dtype(dtype: pl.DataType) -> bool:
204204
return dtype in (pl.Date, pl.Datetime) or isinstance(dtype, pl.Datetime)
205205

206206

207-
def multi_source(
207+
def pushdown_combine(
208208
sources: dict[str, tuple[pl.LazyFrame, dict[str, FilterSpec]]],
209209
combine: Callable[..., pl.LazyFrame],
210210
*,
@@ -303,7 +303,7 @@ def combine(prices, rates, multiplier):
303303
Examples:
304304
Basic usage with lookback::
305305
306-
lf = multi_source(
306+
lf = pushdown_combine(
307307
sources={
308308
"prices": (prices_lf, {
309309
"date": FilterSpec(lookback=timedelta(days=5)),
@@ -332,7 +332,7 @@ def combine_with_region_mapping(sources, region_to_code):
332332
333333
REGION_TO_CODE = {"NORTH_AMERICA": "NA", "EUROPE": "EU"}
334334
335-
lf = multi_source(
335+
lf = pushdown_combine(
336336
sources={
337337
"primary": (primary_lf, {"date": FilterSpec(), "region": FilterSpec()}),
338338
"reference": (reference_lf, {"date": FilterSpec(lookback=timedelta(days=5))}),
@@ -344,7 +344,7 @@ def combine_with_region_mapping(sources, region_to_code):
344344
# Compute output schema for the IO source.
345345
# Wrap in a lambda so that the schema (and the per-source `lf.collect_schema()`
346346
# calls it requires) is only resolved when Polars actually needs it (i.e. at
347-
# collect time), rather than eagerly when `multi_source` is constructed.
347+
# collect time), rather than eagerly when `pushdown_combine` is constructed.
348348
output_schema = lambda: _compute_output_schema(sources, combine, combine_kwargs, sources_as_kwargs)
349349

350350
# Collect all output columns that have FilterSpecs across all sources
@@ -389,7 +389,7 @@ def source_generator(
389389
log.debug(f"Extracted values: {extracted_values}")
390390

391391
# Get source schemas for dtype checking. Resolved lazily here (at
392-
# collect time) rather than eagerly at multi_source construction so
392+
# collect time) rather than eagerly at pushdown_combine construction so
393393
# we don't force schema resolution on each source until we actually
394394
# need it.
395395
source_schemas: dict[str, dict[str, pl.DataType]] = {name: lf.collect_schema() for name, (lf, _) in sources.items()}
@@ -529,7 +529,7 @@ def source_generator(
529529
try:
530530
yield from collect_lf_in_io_source(result_lf, batch_size)
531531
except Exception as e:
532-
err_msg = f"Failed during collection in multi_source.\nPolars plan:\n{result_lf.explain()}\nError: {e.__class__.__name__}: {e}"
532+
err_msg = f"Failed during collection in pushdown_combine.\nPolars plan:\n{result_lf.explain()}\nError: {e.__class__.__name__}: {e}"
533533
raise RuntimeError(err_msg) from e
534534

535535
return register_io_source_with_is_pure(source_generator, schema=output_schema)

polars_io_tools/io_sources/ts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import polars as pl
66

7-
from .multi_source import FilterSpec, multi_source
7+
from .pushdown_combine import FilterSpec, pushdown_combine
88

99
__all__ = ("ts_with_columns",)
1010

@@ -141,7 +141,7 @@ def combine(sources: dict[str, pl.LazyFrame]) -> pl.LazyFrame:
141141
lf = lf.with_columns(expressions)
142142
return lf
143143

144-
return multi_source(
144+
return pushdown_combine(
145145
sources={"main": (self, filter_specs)},
146146
combine=combine,
147147
log_explain=log_explain,

polars_io_tools/testing/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
df = pl.DataFrame({"date": dates, "val": values})
1414
tracker = PredicateTracker(df)
1515
16-
# Use the LazyFrame in your multi_source or IO source
17-
lf = multi_source(
16+
# Use the LazyFrame in your pushdown_combine or IO source
17+
lf = pushdown_combine(
1818
sources={"data": (tracker.lazy_frame, {"date": FilterSpec()})},
1919
combine=lambda s: s["data"],
2020
)

polars_io_tools/testing/predicate_tracker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ class PredicateTracker:
4646
A utility class that creates an IO source which tracks pushed-down predicates.
4747
4848
This is useful for testing that filters are correctly pushed down to sources
49-
in multi_source or other IO source implementations.
49+
in pushdown_combine or other IO source implementations.
5050
5151
Args:
5252
df (pl.DataFrame): The DataFrame to use as the underlying data source.
5353
5454
Attributes:
55-
lazy_frame (pl.LazyFrame): The LazyFrame that can be used in multi_source or other operations.
55+
lazy_frame (pl.LazyFrame): The LazyFrame that can be used in pushdown_combine or other operations.
5656
last_predicate (pl.Expr | None): The last predicate that was pushed down during collection.
5757
last_with_columns (list[str] | None): The last column projection that was pushed down.
5858
call_count (int): Number of times the source has been called.
@@ -61,7 +61,7 @@ class PredicateTracker:
6161
::
6262
6363
tracker = PredicateTracker(df)
64-
lf = multi_source(
64+
lf = pushdown_combine(
6565
sources={"data": (tracker.lazy_frame, {"date": FilterSpec()})},
6666
combine=lambda s: s["data"],
6767
)

polars_io_tools/tests/io_sources/test_pickle.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,11 @@ def test_concat_named_pickle_basic(self):
192192
assert_frame_equal(result, expected)
193193

194194

195-
class TestMultiSourcePickle:
196-
"""Tests for multi_source pickle support."""
195+
class TestPushdownCombinePickle:
196+
"""Tests for pushdown_combine pickle support."""
197197

198-
def test_multi_source_pickle_basic(self):
199-
"""multi_source LazyFrames can be pickled and unpickled."""
198+
def test_pushdown_combine_pickle_basic(self):
199+
"""pushdown_combine LazyFrames can be pickled and unpickled."""
200200
left_df = pl.DataFrame(
201201
{
202202
"date": [date(2025, 1, 1), date(2025, 1, 2)],
@@ -212,7 +212,7 @@ def test_multi_source_pickle_basic(self):
212212
}
213213
)
214214

215-
lf = cpl.multi_source(
215+
lf = cpl.pushdown_combine(
216216
sources={
217217
"left": (left_df.lazy(), {"date": cpl.FilterSpec(), "id": cpl.FilterSpec()}),
218218
"right": (right_df.lazy(), {"date": cpl.FilterSpec(), "id": cpl.FilterSpec()}),

0 commit comments

Comments
 (0)