xarray icon indicating copy to clipboard operation
xarray copied to clipboard

BUG: normalize_indexer breaks _decompose_slice

Open snowman2 opened this issue 1 month ago • 2 comments

What happened?

With xarray==2025.12.0, the rioxarray tests started to fail:

https://github.com/corteva/rioxarray/actions/runs/19997389961/job/57347087949

The issue was traced back to this change #10948.

What did you expect to happen?

No error.

Minimal Complete Verifiable Example

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "xarray[complete]@git+https://github.com/pydata/xarray.git@main",
# ]
# ///
#
# This script automatically imports the development branch of xarray to check for issues.
# Please delete this header if you have _not_ tested this script with `uv run`!

import xarray as xr
xr.show_versions()
from xarray.core.indexing import normalize_indexer, _decompose_slice
print(_decompose_slice(slice(-1, None, -1), 8))
# (slice(0, 8, 1), slice(None, None, -1))
print(_decompose_slice(normalize_indexer(slice(-1, None, -1), 8), 8))
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#     _decompose_slice(normalize_indexer(slice(-1, None, -1), 8), 8)
#     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File ".../lib/python3.14/site-packages/xarray/core/indexing.py", # line 1217, in _decompose_slice
#     exact_stop = range(start, stop, step)[-1]
#                  ~~~~~~~~~~~~~~~~~~~~~~~~^^^^
# IndexError: range object index out of range

Steps to reproduce

No response

MVCE confirmation

  • [x] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
  • [x] Complete example — the example is self-contained, including all data and the text of any traceback.
  • [x] Verifiable example — the example copy & pastes into an IPython prompt or Binder notebook, returning the result.
  • [x] New issue — a search of GitHub Issues suggests this is not a duplicate.
  • [x] Recent environment — the issue occurs with the latest version of xarray and its dependencies.

Relevant log output

=================================== FAILURES ===================================
________________________________ test_indexing _________________________________

    def test_indexing():
...
               # minus-stepped slice
                ind = {"band": numpy.array([2, 1, 0]), "x": slice(-1, None, -1), "y": 0}
>               assert_allclose(expected.isel(**ind), actual.isel(**ind))
...
testenv/lib/python3.12/site-packages/xarray/core/indexing.py:1415: in _decompose_outer_indexer
    bk_slice, np_slice = _decompose_slice(k, s)
                         ^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

key = slice(7, -1, -1), size = 8

    def _decompose_slice(key: slice, size: int) -> tuple[slice, slice]:
            # determine stop precisely for step > 1 case
            # Use the range object to do the calculation
            # e.g. [98:2:-2] -> [98:3:-2]
>           exact_stop = range(start, stop, step)[-1]
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           IndexError: range object index out of range

Anything else we need to know?

No response

Environment

https://github.com/corteva/rioxarray/actions/runs/19997389961/job/57347087949

xarray-2025.12.0 cftime-1.6.5 cloudpickle-3.1.2 dask-2025.11.0 fsspec-2025.12.0 netcdf4-1.7.3

snowman2 avatar Dec 10 '25 22:12 snowman2

I guess the problem is that slice(None, None, -1) is not representable as a concrete slice: s.indices(8) returns (7, -1, -1), which we can feed into range to get the integer indices. However, we can't feed that back into slice because that interprets stop = -1 as "to the end of the sequence", regardless of the step size. Which means the trick we used to normalize slices does not work for negative step sizes.

s = range(3, None, -1)
normalize_slice(s, 8)  # normalized to slice(3, -1, -1)
# which expands to slice(3, 7, -1) (by adding the size to `stop`), which is empty

To fix that, we may have to check for negative step sizes and replace stop with None if it was -1 after normalization.

The only problem I can see with that is that cfgrib once again may not support that, but maybe we should call that a bug in cfgrib, then? @dcherian, what do you think?

keewis avatar Dec 10 '25 23:12 keewis

Yes I believe cfgrib should add support for slice(None). IMO the rioxarray query here is reasonable and we should definitely make it work.

dcherian avatar Dec 11 '25 16:12 dcherian