1515from narwhals ._plan ._parse import parse_into_seq_of_expr_ir
1616from narwhals ._plan .expressions import functions as F , operators as ops
1717from narwhals ._plan .expressions .literal import SeriesLiteral
18+ from narwhals ._plan .expressions .ranges import IntRange
1819from narwhals .exceptions import (
1920 ComputeError ,
2021 InvalidIntoExprError ,
2122 InvalidOperationError ,
2223 InvalidOperationError as LengthChangingExprError ,
24+ MultiOutputExpressionError ,
2325 ShapeError ,
2426)
2527from tests .plan .utils import assert_expr_ir_equal , re_compile
@@ -127,7 +129,7 @@ def test_valid_windows() -> None:
127129 assert nwp .sum_horizontal (a .diff ().abs (), a .cum_sum ()).over (order_by = "i" )
128130
129131
130- def test_invalid_repeat_agg () -> None :
132+ def test_repeat_agg_invalid () -> None :
131133 with pytest .raises (InvalidOperationError ):
132134 nwp .col ("a" ).mean ().mean ()
133135 with pytest .raises (InvalidOperationError ):
@@ -144,7 +146,7 @@ def test_invalid_repeat_agg() -> None:
144146
145147# NOTE: Previously multiple different errors, but they can be reduced to the same thing
146148# Once we are scalar, only elementwise is allowed
147- def test_invalid_agg_non_elementwise () -> None :
149+ def test_agg_non_elementwise_invalid () -> None :
148150 pattern = re .compile (r"cannot use.+rank.+aggregated.+mean" , re .IGNORECASE )
149151 with pytest .raises (InvalidOperationError , match = pattern ):
150152 nwp .col ("a" ).mean ().rank ()
@@ -167,7 +169,7 @@ def test_agg_non_elementwise_range_special() -> None:
167169 assert isinstance (e_ir .expr .input [1 ], ir .Len )
168170
169171
170- def test_invalid_int_range () -> None :
172+ def test_int_range_invalid () -> None :
171173 pattern = re .compile (r"scalar.+agg" , re .IGNORECASE )
172174 with pytest .raises (InvalidOperationError , match = pattern ):
173175 nwp .int_range (nwp .col ("a" ))
@@ -177,16 +179,37 @@ def test_invalid_int_range() -> None:
177179 nwp .int_range (0 , nwp .col ("a" ).abs ())
178180 with pytest .raises (InvalidOperationError , match = pattern ):
179181 nwp .int_range (nwp .col ("a" ) + 1 )
182+ with pytest .raises (InvalidOperationError , match = pattern ):
183+ nwp .int_range ((1 + nwp .col ("b" )).name .keep ())
184+ int_range = IntRange (step = 1 , dtype = nw .Int64 ())
185+ with pytest .raises (InvalidOperationError , match = r"at least 2 inputs.+int_range" ):
186+ int_range .to_function_expr (ir .col ("a" ))
187+
188+
189+ @pytest .mark .xfail (
190+ reason = "Not implemented `int_range(eager=True)`" , raises = NotImplementedError
191+ )
192+ def test_int_range_series () -> None :
193+ assert isinstance (nwp .int_range (50 , eager = True ), nwp .Series )
180194
181195
182- # NOTE: Non-`polars`` rule
183- def test_invalid_over () -> None :
196+ def test_over_invalid () -> None :
197+ with pytest .raises (TypeError , match = r"one of.+partition_by.+or.+order_by" ):
198+ nwp .col ("a" ).last ().over ()
199+
200+ # NOTE: Non-`polars` rule
184201 pattern = re .compile (r"cannot use.+over.+elementwise" , re .IGNORECASE )
185202 with pytest .raises (InvalidOperationError , match = pattern ):
186203 nwp .col ("a" ).fill_null (3 ).over ("b" )
187204
205+ # NOTE: This version isn't elementwise
206+ expr_ir = nwp .col ("a" ).fill_null (strategy = "backward" ).over ("b" )._ir
207+ assert isinstance (expr_ir , ir .WindowExpr )
208+ assert isinstance (expr_ir .expr , ir .FunctionExpr )
209+ assert isinstance (expr_ir .expr .function , F .FillNullWithStrategy )
210+
188211
189- def test_nested_over () -> None :
212+ def test_over_nested () -> None :
190213 pattern = re .compile (r"cannot nest.+over" , re .IGNORECASE )
191214 with pytest .raises (InvalidOperationError , match = pattern ):
192215 nwp .col ("a" ).mean ().over ("b" ).over ("c" )
@@ -196,7 +219,7 @@ def test_nested_over() -> None:
196219
197220# NOTE: This *can* error in polars, but only if the length **actually changes**
198221# The rule then breaks down to needing the same length arrays in all parts of the over
199- def test_filtration_over () -> None :
222+ def test_over_filtration () -> None :
200223 pattern = re .compile (r"cannot use.+over.+change length" , re .IGNORECASE )
201224 with pytest .raises (InvalidOperationError , match = pattern ):
202225 nwp .col ("a" ).drop_nulls ().over ("b" )
@@ -206,9 +229,9 @@ def test_filtration_over() -> None:
206229 nwp .col ("a" ).diff ().drop_nulls ().over ("b" , order_by = "i" )
207230
208231
209- def test_invalid_binary_expr_length_changing () -> None :
232+ def test_binary_expr_length_changing_invalid () -> None :
210233 a = nwp .col ("a" )
211- b = nwp .col ("b" )
234+ b = nwp .col ("b" ). exp ()
212235
213236 with pytest .raises (LengthChangingExprError ):
214237 a .unique () + b .unique ()
@@ -247,7 +270,7 @@ def test_binary_expr_length_changing_agg() -> None:
247270 )
248271
249272
250- def test_invalid_binary_expr_shape () -> None :
273+ def test_binary_expr_shape_invalid () -> None :
251274 pattern = re .compile (
252275 re .escape ("Cannot combine length-changing expressions with length-preserving" ),
253276 re .IGNORECASE ,
@@ -261,6 +284,8 @@ def test_invalid_binary_expr_shape() -> None:
261284 a .map_batches (lambda x : x , is_elementwise = True ) * b .gather_every (1 , 0 )
262285 with pytest .raises (ShapeError , match = pattern ):
263286 a / b .drop_nulls ()
287+ with pytest .raises (ShapeError , match = pattern ):
288+ a .fill_null (1 ) // b .rolling_mean (5 )
264289
265290
266291@pytest .mark .parametrize ("into_iter" , [list , tuple , deque , iter , dict .fromkeys , set ])
@@ -306,7 +331,7 @@ def test_is_in_series() -> None:
306331 ),
307332 ],
308333)
309- def test_invalid_is_in (other : Any , context : AbstractContextManager [Any ]) -> None :
334+ def test_is_in_invalid (other : Any , context : AbstractContextManager [Any ]) -> None :
310335 with context :
311336 nwp .col ("a" ).is_in (other )
312337
0 commit comments