code: def _normalize_index(
      shape: tuple[int, ...], idx: Any
  ) -> tuple[slice, ...]:
      # Convert idx to type tuple[slice, ...] with length == ndim.
      # start, stop, step of each slice are set to a non-None value.
      if not isinstance(idx, tuple):
          idx = (idx,)
  
      ndim = len(shape)
      if len(idx) > ndim:
          raise IndexError(
              "too many indices for array:"
              f" array is {ndim}-dimensional, but {len(idx)} were indexed"
          )
      idx = idx + (slice(None),) * (ndim - len(idx))
  
      new_idx = []
      for i in range(ndim):
          if isinstance(idx[i], int):
              if idx[i] >= shape[i]:
                  raise IndexError(
                      f"Index {idx[i]} is out of bounds"
                      f" for axis {i} with size {shape[i]}"
                  )
              new_idx.append(slice(idx[i], idx[i] + 1, 1))
          elif isinstance(idx[i], slice):
              start, stop, step = idx[i].indices(shape[i])
              if step <= 0:
                  raise ValueError("Slice step must be positive.")
              if start == stop:
                  raise ValueError(f"The index is empty on axis {i}")
              new_idx.append(slice(start, stop, step))
          else:
              raise ValueError(f"Invalid index on axis {i}")
  
      return tuple(new_idx)
  
file_path: cupy/cupyx/distributed/array/_index_arith.py
imports: import typing
  from typing import Any, Optional
label_desc: Use image node_id 8 for calling a global function with example usage: _normalize_index(shape, idx) and returns: tuple