docs: def _clean_slice(key, length):
      """
      Validates and normalizes a cell range slice.
  
      >>> _clean_slice(slice(None, None), 10)
      (0, 10)
      >>> _clean_slice(slice(-10, 10), 10)
      (0, 10)
      >>> _clean_slice(slice(-11, 11), 10)
      (0, 10)
      >>> _clean_slice(slice('x', 'y'), 10)
      Traceback (most recent call last):
      ...
      TypeError: Cell indices must be integers, str given.
      >>> _clean_slice(slice(0, 10, 2), 10)
      Traceback (most recent call last):
      ...
      NotImplementedError: Cell slice with step is not supported.
      >>> _clean_slice(slice(5, 5), 10)
      Traceback (most recent call last):
      ...
      ValueError: Cell slice can not be empty.
  
      """
      if key.step is not None:
          raise NotImplementedError('Cell slice with step is not supported.')
      start, stop = key.start, key.stop
      if start is None:
          start = 0
      if stop is None:
          stop = length
      if not isinstance(start, integer_types):
          raise TypeError('Cell indices must be integers, %s given.' % type(start).__name__)
      if not isinstance(stop, integer_types):
          raise TypeError('Cell indices must be integers, %s given.' % type(stop).__name__)
      if start < 0:
          start = start + length
      if stop < 0:
          stop = stop + length
      start, stop = max(0, start), min(length, stop)
      if start == stop:
          raise ValueError('Cell slice can not be empty.')
      return start, stop