Skip to content
Draft
Show file tree
Hide file tree
Changes from 118 commits
Commits
Show all changes
528 commits
Select commit Hold shift + click to select a range
3a1c375
refactor: Simplify `FunctionExpr` version
dangotbanned Jun 24, 2025
bd5c33f
fix: Ensure `output_name` matches upstream
dangotbanned Jun 24, 2025
f0d9ddc
Merge branch 'main' into oh-nodes
dangotbanned Jun 24, 2025
a84ae05
feat: Add `NamedIR.(__repr__|_repr_html_)`
dangotbanned Jun 24, 2025
a46b3e5
test: Add `test_rewrite_elementwise_over_complex`
dangotbanned Jun 24, 2025
83e2b58
fix: Handle `*args` in `rewrite_elementwise_over`
dangotbanned Jun 24, 2025
ac2b1ff
feat: Add `int_range`
dangotbanned Jun 25, 2025
cb8234e
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 25, 2025
7324753
feat: Add `NamedIR.is_elementwise_top_level`
dangotbanned Jun 25, 2025
5114d32
Merge branch 'main' into oh-nodes
dangotbanned Jun 25, 2025
afd0bc7
Merge branch 'main' into oh-nodes
dangotbanned Jun 25, 2025
1f6e1da
feat: Initial `rewrite_binary_agg_over` impl
dangotbanned Jun 26, 2025
f2f6141
Merge branch 'oh-nodes' of https://github.com/narwhals-dev/narwhals i…
dangotbanned Jun 26, 2025
f2ac1c6
fix: undo `TypeIs` import
dangotbanned Jun 26, 2025
46831ea
Merge branch 'main' into oh-nodes
dangotbanned Jun 26, 2025
e726865
fix: Ensure lhs gets leaf name used
dangotbanned Jun 26, 2025
6edc52c
test: Add some `rewrite_binary_agg_over`
dangotbanned Jun 26, 2025
5c80033
Merge branch 'main' into oh-nodes
dangotbanned Jun 29, 2025
4076d4d
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 2, 2025
463d75a
feat: `FrozenSchema` repr#
dangotbanned Jul 2, 2025
08dcfca
refactor: Split out `FrozenSchema`
dangotbanned Jul 2, 2025
17822e8
planning schema projection
dangotbanned Jul 2, 2025
8243433
revert: Drop unplanned `impl_arrow` bits
dangotbanned Jul 2, 2025
984c07b
fix(typing): `*Series` generic
dangotbanned Jul 2, 2025
1468662
ci: Ignore dtypes import
dangotbanned Jul 2, 2025
2985bd5
feat: Reimpl `pyarrow`, start on `select`
dangotbanned Jul 2, 2025
a85fc7e
feat(pyarrow): Impl `Cast`, `Sort`, `Filter`, `Len`
dangotbanned Jul 2, 2025
faa91ec
feat(pyarrow): Impl `SortBy`
dangotbanned Jul 2, 2025
e6fab3f
Merge branch 'main' into oh-nodes
dangotbanned Jul 3, 2025
25ba870
Merge branch 'main' into oh-nodes
dangotbanned Jul 3, 2025
925d601
feat(pyarrow): Impl `First`, `Last`
dangotbanned Jul 3, 2025
c823d78
feat(pyarrow): Impl all aggregations
dangotbanned Jul 3, 2025
b069348
docs: Note on broacasting
dangotbanned Jul 3, 2025
a4d90d8
feat(DRAFT): Prepare new broadcasting layer
dangotbanned Jul 3, 2025
0ba05eb
refactor: Move `flatten_hash_safe`
dangotbanned Jul 4, 2025
3a4776f
more giant refactors 😅
dangotbanned Jul 4, 2025
5ca704f
ignore-banned-import
dangotbanned Jul 4, 2025
8e40fea
fix: alias removed exception types
dangotbanned Jul 4, 2025
34355dc
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 4, 2025
e45118d
refactor: Handle expansion, projection at narwhals-level
dangotbanned Jul 4, 2025
86d56ad
feat: Add `NamedIR.from_name`
dangotbanned Jul 5, 2025
a193af0
test: Add `test_lit_series_roundtrip`
dangotbanned Jul 5, 2025
8e47788
fix(typing): Propagate `NativeSeriesT`
dangotbanned Jul 5, 2025
9e2e2b9
chore(typing): Link to pyright explainer
dangotbanned Jul 5, 2025
154a3a0
test: Update tests that shouldn't broadcast
dangotbanned Jul 5, 2025
6bf86d8
keep on iterating
dangotbanned Jul 5, 2025
f2c6566
fix: Fill in missing type params
dangotbanned Jul 6, 2025
423ea9a
refactor: Move `native` out of higher protocol
dangotbanned Jul 6, 2025
f47fed2
refactor: Impl dispatch only once?
dangotbanned Jul 6, 2025
a4b1a02
chore: planning `CompliantNamespace`
dangotbanned Jul 6, 2025
1aaca2a
feat: `col`, `lit` classmethods?
dangotbanned Jul 6, 2025
1060f0e
feat(DRAFT): Dispatch take ✌
dangotbanned Jul 6, 2025
d6ebf9b
Update narwhals/_plan/dummy.py
dangotbanned Jul 6, 2025
33ddb8d
maybe `pyarrow` backcompat?
dangotbanned Jul 6, 2025
7aa7d1d
is `len` the issue?
dangotbanned Jul 6, 2025
eebef7a
plz
dangotbanned Jul 6, 2025
acdbf5e
revert: remove typing check
dangotbanned Jul 7, 2025
4437861
fix: Unwrap scalar on old pyarrow
dangotbanned Jul 7, 2025
5b6e644
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 7, 2025
bcdca6e
fix: Use new `Interval` helper in `truncate`
dangotbanned Jul 7, 2025
96b0c9c
feat(pyarrow): Impl `len`
dangotbanned Jul 7, 2025
9583383
feat(pyarrow): More impls
dangotbanned Jul 7, 2025
5457b30
test(pyarrow): Coverage for most of the current impl
dangotbanned Jul 7, 2025
c0c6b7d
refactor(pyarrow): Migrate most of `evaluate` into `ArrowExpr`
dangotbanned Jul 7, 2025
5039a09
feat(pyarrow): Add `ArrowDataFrame.sort`
dangotbanned Jul 7, 2025
c26c862
feat(pyarrow): Impl `Expr.sort_by`
dangotbanned Jul 7, 2025
c720749
fix: unused-ignore
dangotbanned Jul 7, 2025
017aa57
Merge branch 'main' into oh-nodes
dangotbanned Jul 8, 2025
8dcc57b
refactor: Move `lit`, `col`, `len` to namespace
dangotbanned Jul 8, 2025
4d54ff4
refactor: Move `len` impl to `EagerNamespace`
dangotbanned Jul 8, 2025
25a248b
revert: remove unused
dangotbanned Jul 8, 2025
d682390
feat: Add the remaining top-level nodes
dangotbanned Jul 8, 2025
6fdbd34
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 9, 2025
14033ae
refactor: Use `Protocol`s, move to `protocols`
dangotbanned Jul 9, 2025
d72b200
feat(pyarrow): Impl `BinaryExpr`
dangotbanned Jul 9, 2025
d5ccd31
🧹🧹🧹
dangotbanned Jul 9, 2025
a40b044
fix: Align all binary ops with `polars`
dangotbanned Jul 10, 2025
1880cef
fix: Fill in some `DType` holes
dangotbanned Jul 10, 2025
4b5b9ab
test: use `assert_equal_data`
dangotbanned Jul 10, 2025
2c0c067
refactor: tighten up protocols
dangotbanned Jul 10, 2025
97b84fd
Merge branch 'main' into oh-nodes
dangotbanned Jul 11, 2025
cb2e247
refactor: rename `Agg` -> `AggExpr`
dangotbanned Jul 11, 2025
4947ec4
fix: Check for scalars in `int_range`
dangotbanned Jul 11, 2025
5c33fce
feat: Identify n-ary functions
dangotbanned Jul 11, 2025
3c5663a
feat(DRAFT): Stub out namespace functions
dangotbanned Jul 11, 2025
f48d868
fix(typing): Oops they return expr
dangotbanned Jul 11, 2025
08e2bd2
feat(pyarrow): Impl `int_range`
dangotbanned Jul 11, 2025
5635be2
fix: typo in error message
dangotbanned Jul 11, 2025
98fe85a
feat(pyarrow): Impl `pow`
dangotbanned Jul 12, 2025
63b63c8
feat(pyarrow): Impl `fill_null`, `is_between`
dangotbanned Jul 12, 2025
cccc323
feat(pyarrow): Impl 6x boolean unary functions
dangotbanned Jul 12, 2025
1ad79a1
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 12, 2025
638b585
chore: remove `to_compliant`/`_to_compliant`
dangotbanned Jul 12, 2025
ccfa7da
rename `to_compliant_test` -> `compliant_test`
dangotbanned Jul 12, 2025
df52814
test: Add failing `when` tests
dangotbanned Jul 13, 2025
3140a48
feat(pyarrow): Impl complex `when-then-otherwise`
dangotbanned Jul 13, 2025
d9d8e64
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 14, 2025
b5550b4
refactor: Remove `pyarrow<13` compat
dangotbanned Jul 14, 2025
97453bb
refactor: Moving around
dangotbanned Jul 14, 2025
fbb463b
feat(pyarrow): Impl `all_horizontal`
dangotbanned Jul 14, 2025
21bf3db
feat(pyarrow): Impl `{any,sum,min,max}_horizontal`
dangotbanned Jul 14, 2025
0ae9f5f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 15, 2025
ec06a5e
test: Shorten some test ids
dangotbanned Jul 15, 2025
031d547
test(pyarrow): Add `any_horizontal` tests
dangotbanned Jul 15, 2025
51ed0fb
test: Add equiv to `test_sumh_broadcasting`
dangotbanned Jul 15, 2025
b065573
feat(pyarrow): Impl `mean_horizontal`
dangotbanned Jul 15, 2025
3732e5a
fix: fill nulls in `sum_horizontal`
dangotbanned Jul 15, 2025
2b44d63
test: Add detail to xfail message
dangotbanned Jul 15, 2025
17634cd
refactor: Split out `functions`, `typing`
dangotbanned Jul 16, 2025
02092b4
feat(pyarrow): Impl `concat_str`
dangotbanned Jul 16, 2025
5d57676
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 16, 2025
3db1e65
prep for complex nodes
dangotbanned Jul 16, 2025
a5f110f
feat(pyarrow): Impl `map_batches`
dangotbanned Jul 16, 2025
747a5ae
test: Add `map_batches` tests
dangotbanned Jul 17, 2025
e85af02
refactor(pyarrow): Split out `int_range`
dangotbanned Jul 17, 2025
088a48a
feat(DRAFT): Add `with_columns`
dangotbanned Jul 17, 2025
168e930
Merge branch 'main' into oh-nodes
dangotbanned Jul 18, 2025
cf2f96c
fix: Extend exprs in `Schema._with_columns`
dangotbanned Jul 18, 2025
cd6ade0
wip: add `concat`
dangotbanned Jul 18, 2025
76b9b9a
refactor: Split out eager, remove `from_series`
dangotbanned Jul 18, 2025
37d7709
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 18, 2025
250922f
revert: Leave out unused `schema_projected` for now
dangotbanned Jul 23, 2025
8729ebe
refactor(typing): Relax `_concat_horizontal`
dangotbanned Jul 23, 2025
6ef9375
feat(pyarrow): Impl `over_ordered`
dangotbanned Jul 23, 2025
d3bed76
oop
dangotbanned Jul 23, 2025
cf38656
Merge branch 'main' into oh-nodes
dangotbanned Jul 23, 2025
2c85701
add `_with_columns`
dangotbanned Jul 30, 2025
4de8579
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jul 30, 2025
94d533d
perf: Add early return path for `over_ordered`
dangotbanned Jul 30, 2025
c893b2f
Merge branch 'main' into oh-nodes
dangotbanned Aug 2, 2025
b03d31f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 15, 2025
25549de
style(ruff): re-run updated config
dangotbanned Aug 15, 2025
3ec5f7b
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 19, 2025
a12a985
chore(ruff): re-run updated config x2
dangotbanned Aug 19, 2025
62030ae
refactor(expr-ir): Rename `Dummy*` everything (#3014)
dangotbanned Aug 19, 2025
25b744d
refactor(expr-ir): Heavily sugar `Function` defs (#3017)
dangotbanned Aug 21, 2025
fcfcabf
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 21, 2025
de884c5
refactor(typing): Align `NativeDataFrame`
dangotbanned Aug 21, 2025
dbe2a45
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 25, 2025
f5ecc6e
refactor(expr-ir): Add more builder sugar (#3040)
dangotbanned Aug 25, 2025
69eede5
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 28, 2025
c934d25
refactor(expr-ir): Clearing out the cobwebs (#3053)
dangotbanned Aug 29, 2025
dc3b9ea
Merge branch 'main' into oh-nodes
dangotbanned Aug 29, 2025
607ea5a
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Aug 30, 2025
631d3a3
refactor(expr-ir): `copy.replace` most `with_*` methods (#3063)
dangotbanned Aug 31, 2025
192de64
Merge branch 'main' into oh-nodes
dangotbanned Sep 1, 2025
215386f
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 5, 2025
973824c
refactor(expr-ir): Shrinking `ExprIR` (main) (#3066)
dangotbanned Sep 10, 2025
70744e0
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 10, 2025
6bc5ff5
fix(typing): Make pyright mostly happy
dangotbanned Sep 10, 2025
2aa8a2e
chore(typing): Ignore the other one for now
dangotbanned Sep 10, 2025
53d314d
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 11, 2025
967a150
refactor(expr-ir): Organize `_plan` package (#3122)
dangotbanned Sep 14, 2025
318261e
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Sep 15, 2025
7599fc4
revert(ruff): ignore (`RUF043`)
dangotbanned Sep 15, 2025
e502dcc
Merge branch 'main' into oh-nodes
dangotbanned Sep 18, 2025
65f9738
Merge branch 'main' into oh-nodes
dangotbanned Sep 21, 2025
8208d32
feat(expr-ir): Support `group_by`, utilize `pyarrow.acero` (#3143)
dangotbanned Oct 1, 2025
53f4041
Merge branch 'main' into oh-nodes
dangotbanned Oct 1, 2025
2b9dbf0
refactor(expr-ir): Split up and refine `protocols.py` (#3166)
dangotbanned Oct 1, 2025
2403f1b
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 6, 2025
bde22ac
feat(expr-ir): Acero `order_by`, `hashjoin` , `DataFrame.{filter,join…
dangotbanned Oct 12, 2025
cce4289
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 12, 2025
1a433a9
fix: Update alias import
dangotbanned Oct 12, 2025
bf544d4
fix(expr-ir): Ensure only `__slots__`, and not `__dict__` too (#3201)
dangotbanned Oct 13, 2025
63c85c8
Merge branch 'main' into oh-nodes
dangotbanned Oct 13, 2025
fa0899a
perf(expr-ir): Optimize `ExpansionFlags.from_ir` (#3206)
dangotbanned Oct 15, 2025
1ab1599
Merge branch 'main' into oh-nodes
dangotbanned Oct 15, 2025
34a1fd0
refactor(expr-ir): Improve function dispatch (#3215)
dangotbanned Oct 18, 2025
dbca5a5
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 18, 2025
9acce69
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Oct 20, 2025
46abfe6
chore: re-sync imports following (#3086)
dangotbanned Oct 20, 2025
1ba04b1
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Nov 1, 2025
089bfb5
Merge branch 'main' into oh-nodes
dangotbanned Nov 2, 2025
adfaf1a
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Nov 4, 2025
0a66899
feat(expr-ir): Support `over(*partition_by)` (#3224)
dangotbanned Nov 7, 2025
2ae7eab
Merge branch 'main' into oh-nodes
dangotbanned Nov 7, 2025
80126e0
feat(expr-ir): Add `date_range`, support `{date,int}_range(eager=...)…
dangotbanned Nov 8, 2025
b7e1160
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Nov 8, 2025
3a6c25a
feat(expr-ir): Add `{DataFrame,Series}.to_polars` (#3303)
dangotbanned Nov 11, 2025
944fffc
feat(expr-ir): Impl `rank`, `with_row_index_by`, `over(*partition_by,…
dangotbanned Nov 19, 2025
26e4381
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Nov 19, 2025
069d594
Merge branch 'main' into oh-nodes
dangotbanned Nov 26, 2025
8536be1
Merge branch 'main' into oh-nodes
dangotbanned Dec 13, 2025
1618650
feat(expr-ir): Finish* implementing `ArrowExpr` (#3325)
dangotbanned Dec 14, 2025
943fa9d
Merge branch 'main' into oh-nodes
dangotbanned Dec 14, 2025
1550feb
chore(expr-ir): Re-sync `over` with current `polars` (#3352)
dangotbanned Dec 14, 2025
fcb3369
feat(expr-ir): Add `list.*` aggregate methods (#3353)
dangotbanned Dec 17, 2025
149bae5
Merge branch 'main' into oh-nodes
dangotbanned Dec 17, 2025
9b9122b
feat(expr-ir): Add `list.sort` (#3359)
dangotbanned Dec 18, 2025
158eda2
feat(expr-ir): Impl `DataFrame.unique` (#3364)
dangotbanned Dec 22, 2025
d47a7fa
feat(expr-ir): Add `BaseFrame.unpivot` (#3368)
dangotbanned Dec 22, 2025
4c5ed8a
feat(expr-ir): Add `DataFrame.write_{csv,parquet}` (#3369)
dangotbanned Dec 23, 2025
2c1e9af
feat(expr-ir): Add `read_{csv,parquet}` (#3370)
dangotbanned Dec 24, 2025
59b6a5f
feat(expr-ir): Add `concat` (#3371)
dangotbanned Dec 24, 2025
f05fa2a
refactor: Move compatibility flags
dangotbanned Dec 24, 2025
f6ac633
fix(expr-ir): Reject `Selector`s that do more than select (#3376)
dangotbanned Dec 30, 2025
a1a1645
feat(expr-ir): Support `DataFrame.pivot` (#3373)
dangotbanned Dec 30, 2025
058782d
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Dec 30, 2025
8d6101c
feat(expr-ir): Support `DataFrame.join_asof` (#3378)
dangotbanned Dec 31, 2025
464279d
test(expr-ir): Add some `shift` coverage
dangotbanned Jan 3, 2026
6dd74fe
refactor(expr-ir): Make `arrow.functions` a package (#3384)
dangotbanned Jan 3, 2026
6713331
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jan 4, 2026
56f58ef
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jan 23, 2026
2bb7442
feat(expr-ir): Add `BaseFrame.unnest` (#3414)
dangotbanned Jan 23, 2026
31aadc4
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jan 24, 2026
dc7fb95
feat(expr-ir): Add `selectors.decimal` (#3418)
dangotbanned Jan 24, 2026
0d81b8b
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Feb 2, 2026
230fbfe
refactor(perf): Side-port (#3435) `{min,max}_horizontal` fix
dangotbanned Feb 2, 2026
651b927
refactor(expr-ir): Be less kind in `SupportsBroadcast.align`
dangotbanned Feb 4, 2026
eebb634
revert: `check_docstrings.py` changes
dangotbanned Feb 6, 2026
12c9922
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Feb 6, 2026
55f267c
feat: Add `DataFrame.clone`
dangotbanned Dec 20, 2025
0d8d81b
refactor: Port `all`, `all_horizontal` sugar from `LogicalPlan`
dangotbanned Feb 6, 2026
ef8f7d5
Merge branch 'main' into oh-nodes
dangotbanned Feb 7, 2026
2e84bc1
Merge branch 'main' into oh-nodes
dangotbanned Apr 4, 2026
61baf17
feat(expr-ir): Long-overdue sync (#3611)
dangotbanned May 11, 2026
8c76833
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned May 11, 2026
b322ce9
chore: Drop support for python `3.9`
dangotbanned May 11, 2026
1493e34
Merge branch 'main' into oh-nodes
dangotbanned May 12, 2026
a2beffb
fix(expr-ir): Add compat for old polars (#3617)
dangotbanned May 13, 2026
474882c
fix(expr-ir): Backport `__replace__` typing for `<3.13` (#3620)
dangotbanned May 13, 2026
06666b0
ci(expr-ir): Fix coverage/tests in ci (#3622)
dangotbanned May 14, 2026
8d491b0
fix(expr-ir): Add more compat for old `polars` (#3624)
dangotbanned May 14, 2026
1dddb85
refactor: Rename `input` -> `args`
dangotbanned May 15, 2026
10857f9
feat: Display idomatic name in `LitSeries` repr
dangotbanned May 15, 2026
0af706d
feat(expr-ir): Improve `lit` repr (#3641)
dangotbanned May 19, 2026
5dee22d
Squashed commit of the following:
dangotbanned May 19, 2026
ee795d8
Revert "Squashed commit of the following:"
dangotbanned May 19, 2026
33c8d51
docs(expr-ir): Explain `FunctionExpr` (#3645)
dangotbanned May 21, 2026
85c9123
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned May 21, 2026
05c940f
chore: move `_plan` to `src/narwhals/`
dangotbanned May 21, 2026
8775915
fix: Avoid local `.gitignore` bug
dangotbanned May 21, 2026
7b7bb58
ci: add `.git-blame-ignore-revs`
dangotbanned May 21, 2026
08a2eda
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 21, 2026
8d0ab34
chore: touch files that were missing
dangotbanned May 21, 2026
d482fff
Merge branch 'main' into oh-nodes
dangotbanned May 25, 2026
edc349a
feat(expr-ir): Add top-level `select` (#3647)
dangotbanned May 25, 2026
c5719de
fix(expr-ir): Allow `struct.field` to select from it's own context (#…
dangotbanned May 25, 2026
32ebac4
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned May 26, 2026
3b99e95
feat(expr-ir): Almost finish `PolarsExpr` (#3659)
dangotbanned May 30, 2026
49dde3a
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned May 30, 2026
42ff1cd
fix: Use a valid entrypoint group name
dangotbanned May 30, 2026
bf4c54c
ci: try to prevent more merge conflicts
dangotbanned May 30, 2026
a14492c
ci: end of line
dangotbanned May 30, 2026
8af89be
fix(expr-ir): Ensure `over(order_by=...)` always broadcasts (#3662)
dangotbanned May 31, 2026
c15eb9f
Merge branch 'main' into oh-nodes
dangotbanned May 31, 2026
ece0f63
feat(expr-ir): Add top-level `struct` function (#3666)
dangotbanned Jun 3, 2026
c57e72c
Merge remote-tracking branch 'upstream/main' into oh-nodes
dangotbanned Jun 3, 2026
c179a78
docs(expr-ir): Add API Reference (#3777)
dangotbanned Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
hooks:
- id: codespell
files: \.(py|rst|md)$
args: [--ignore-words-list=ser]
args: [--ignore-words-list=ser,rightt]
Comment thread
dangotbanned marked this conversation as resolved.
Outdated
exclude: ^docs/api-completeness.md$
- repo: https://github.com/pycqa/flake8
rev: '7.2.0' # todo: remove once https://github.com/astral-sh/ruff/issues/458 is addressed
Expand Down
41 changes: 41 additions & 0 deletions narwhals/_plan/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Brainstorming an `Expr` internal representation.

Notes:
- Each `Expr` method should be representable by a single node
- But the node does not need to be unique to the method
- A chain of `Expr` methods should form a plan of operations
- We must be able to enforce rules on what plans are permitted:
- Must be flexible to both eager/lazy and individual backends
- Must be flexible to a given context (select, with_columns, filter, group_by)
- Nodes & plans are:
- Immutable, but
- Can be extended/re-written at both the Narwhals & Compliant levels
- Introspectable, but
- Store as little-as-needed for the common case
- Provide properties/methods for computing the less frequent metadata

References:
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-python/src/lazyframe/visitor/expr_nodes.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/expr.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/function_expr/mod.rs
- https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/options/mod.rs#L137-L172
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L35-L106
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L131-L236
- https://github.com/pola-rs/polars/blob/3fd7ecc5f9de95f62b70ea718e7e5dbf951b6d1c/crates/polars-plan/src/plans/options.rs#L240-L267
- https://github.com/pola-rs/polars/blob/6df23a09a81c640c21788607611e09d9f43b1abc/crates/polars-plan/src/plans/aexpr/mod.rs

Related:
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2866902903
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2867331343
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2867446959
- https://github.com/narwhals-dev/narwhals/pull/2483#issuecomment-2869070157
- (https://github.com/narwhals-dev/narwhals/pull/2538/commits/a7eeb0d23e67cb70e7cfa73cec2c7b69a15c8bef#r2083562677)
- https://github.com/narwhals-dev/narwhals/issues/2225
- https://github.com/narwhals-dev/narwhals/issues/1848
- https://github.com/narwhals-dev/narwhals/issues/2534#issuecomment-2875676729
- https://github.com/narwhals-dev/narwhals/issues/2291
- https://github.com/narwhals-dev/narwhals/issues/2522
- https://github.com/narwhals-dev/narwhals/pull/2555
"""

from __future__ import annotations
95 changes: 95 additions & 0 deletions narwhals/_plan/aggregation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from narwhals._plan.common import ExprIR

if TYPE_CHECKING:
from typing import Iterator

from narwhals.typing import RollingInterpolationMethod


class Agg(ExprIR):
__slots__ = ("expr",)

expr: ExprIR

@property
def is_scalar(self) -> bool:
return True

def __repr__(self) -> str:
tp = type(self)
if tp in {Agg, OrderableAgg}:
return tp.__name__
m = {ArgMin: "arg_min", ArgMax: "arg_max", NUnique: "n_unique"}
name = m.get(tp, tp.__name__.lower())
return f"{self.expr!r}.{name}()"

def iter_left(self) -> Iterator[ExprIR]:
yield from self.expr.iter_left()
yield self

def iter_right(self) -> Iterator[ExprIR]:
yield self
yield from self.expr.iter_right()


class Count(Agg): ...


class Max(Agg): ...


class Mean(Agg): ...


class Median(Agg): ...


class Min(Agg): ...


class NUnique(Agg): ...


class Quantile(Agg):
__slots__ = (*Agg.__slots__, "interpolation", "quantile")

quantile: float
interpolation: RollingInterpolationMethod


class Std(Agg):
__slots__ = (*Agg.__slots__, "ddof")

ddof: int
"""https://github.com/narwhals-dev/narwhals/pull/2555"""


class Sum(Agg): ...


class Var(Agg):
__slots__ = (*Agg.__slots__, "ddof")

ddof: int
"""https://github.com/narwhals-dev/narwhals/pull/2555"""


class OrderableAgg(Agg): ...


class First(OrderableAgg):
"""https://github.com/narwhals-dev/narwhals/issues/2526."""


class Last(OrderableAgg):
"""https://github.com/narwhals-dev/narwhals/issues/2526."""


class ArgMin(OrderableAgg): ...


class ArgMax(OrderableAgg): ...
141 changes: 141 additions & 0 deletions narwhals/_plan/boolean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
from __future__ import annotations

# NOTE: Needed to avoid naming collisions
# - Any
import typing as t

from narwhals._plan.common import Function
from narwhals._plan.options import FunctionFlags, FunctionOptions

if t.TYPE_CHECKING:
from narwhals.typing import ClosedInterval


class BooleanFunction(Function):
def __repr__(self) -> str:
tp = type(self)
if tp is BooleanFunction:
return tp.__name__
m: dict[type[BooleanFunction], str] = {
All: "all",
Any: "any",
AllHorizontal: "all_horizontal",
AnyHorizontal: "any_horizontal",
IsBetween: "is_between",
IsDuplicated: "is_duplicated",
IsFinite: "is_finite",
IsNan: "is_nan",
IsNull: "is_null",
IsFirstDistinct: "is_first_distinct",
IsLastDistinct: "is_last_distinct",
IsUnique: "is_unique",
IsIn: "is_in",
Not: "not",
}
return m[tp]


class All(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.aggregation()


class AllHorizontal(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise().with_flags(
FunctionFlags.INPUT_WILDCARD_EXPANSION
)


class Any(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.aggregation()


class AnyHorizontal(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise().with_flags(
FunctionFlags.INPUT_WILDCARD_EXPANSION
)


class IsBetween(BooleanFunction):
"""`lower_bound`, `upper_bound` aren't spec'd in the function enum.

Assuming the `FunctionExpr.input` becomes `s` in the impl

https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/boolean.rs#L225-L237
"""

__slots__ = ("closed",)

closed: ClosedInterval

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsDuplicated(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsFinite(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsFirstDistinct(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsIn(BooleanFunction):
"""``other` isn't spec'd in the function enum.

See `IsBetween` comment.
"""

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsLastDistinct(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class IsNan(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsNull(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()


class IsUnique(BooleanFunction):
@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.length_preserving()


class Not(BooleanFunction):
"""`__invert__`."""

@property
def function_options(self) -> FunctionOptions:
return FunctionOptions.elementwise()
19 changes: 19 additions & 0 deletions narwhals/_plan/categorical.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import annotations

from narwhals._plan.common import Function
from narwhals._plan.options import FunctionOptions


class CategoricalFunction(Function): ...


class GetCategories(CategoricalFunction):
"""https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/cat.rs#L7."""

@property
def function_options(self) -> FunctionOptions:
"""https://github.com/pola-rs/polars/blob/62257860a43ec44a638e8492ed2cf98a49c05f2e/crates/polars-plan/src/dsl/function_expr/cat.rs#L41."""
return FunctionOptions.groupwise()

def __repr__(self) -> str:
return "cat.get_categories"
Loading