@@ -77,12 +77,15 @@ def rewrite_slice(node: nodes.BigFrameNode):
7777
7878 slice_def = (node .start , node .stop , node .step )
7979
80- # Handle empty slice cases explicitly before any row count dependent logic
81- # This ensures empty slices always return empty results regardless of statistics
82- if _is_empty_slice (node .start , node .stop , node .step ):
83- # Create a filter that will always return empty results
84- # Use start=0, stop=0, step=1 to ensure empty result
85- return slice_as_filter (node .child , 0 , 0 , 1 )
80+ # Handle empty slice cases exlicitly (e.g. [0:0])
81+ if (
82+ node .start is not None
83+ and node .stop is not None
84+ and node .start >= node .stop
85+ and (node .step is None or node .step > 0 )
86+ ):
87+ # Return empty result by filtering with impossible condition
88+ return slice_as_filter (node .child , node .start , node .start , node .step or 1 )
8689
8790 # no-op (eg. df[::1])
8891 if slices .is_noop (slice_def , node .child .row_count ):
@@ -97,26 +100,6 @@ def rewrite_slice(node: nodes.BigFrameNode):
97100 return slice_as_filter (node .child , * slice_def )
98101
99102
100- def _is_empty_slice (start , stop , step ):
101- """Check if a slice will always return empty results."""
102- if start is None or stop is None :
103- return False
104-
105- # Normalize step
106- if step is None :
107- step = 1
108-
109- # For positive step, empty if start >= stop
110- # For negative step, empty if start <= stop
111- if step > 0 :
112- return start >= stop
113- elif step < 0 :
114- return start <= stop
115- else :
116- # step == 0 is invalid, but handle gracefully
117- return True
118-
119-
120103def slice_as_filter (
121104 node : nodes .BigFrameNode , start : Optional [int ], stop : Optional [int ], step : int
122105) -> nodes .BigFrameNode :
0 commit comments