class_docstr: This class is a symbolic representation of a TensorList and is used at graph definition
  stage. It does not carry actual data, but is used to define the connections between operators
  and to specify the pipeline outputs. See documentation for :class:`Pipeline` for details.
  
  ``DataNode`` objects can be passed to DALI operators as inputs (and some of the named keyword
  arguments) but they also provide arithmetic operations which implicitly create appropriate
  operators that perform the expressions.
code: def __getitem__(self, val) -> DataNode:
      idxs = []
      new_axes = []
      new_axis_names = []
  
      # returns True if this index adds a new output dimension
      def process_index(idx, dim):
          if idx is None:
              idxs.append((None, None, None, None))
              return True
          elif isinstance(idx, slice):
              idxs.append((None, idx.start, idx.stop, idx.step))
              return True
          if isinstance(idx, _NewAxis):
              new_axes.append(dim)
              if idx.name is not None:
                  new_axis_names.append(idx.name)
              return True
          if idx is Ellipsis:
              raise NotImplementedError(
                  "Ellipsis in indexing is not implemented"
              )
          if isinstance(idx, (float, str)):
              raise TypeError("Invalid type for an index: ", type)
          idxs.append((idx, None, None, None))
          return False
  
      if not isinstance(val, tuple):
          val = (val,)
      d = 0
      for v in val:
          if process_index(v, d):
              d += 1
  
      if len(new_axis_names) != 0:
          if len(new_axis_names) != len(new_axes):
              raise ValueError(
                  "New axis name must be specified for all axes or none."
              )
          new_axis_names = "".join(new_axis_names)
      else:
          new_axis_names = None
  
      slice_args = {}
      for i, (at, lo, hi, step) in enumerate(idxs):
          if at is not None:
              slice_args["at_%i" % i] = at
          if lo is not None:
              slice_args["lo_%i" % i] = lo
          if hi is not None:
              slice_args["hi_%i" % i] = hi
          if step is not None:
              slice_args["step_%i" % i] = step
  
      import nvidia.dali.fn
  
      if len(slice_args) == 0:
          # No true slicing arguments - only full range : and dali.newaxis.
          # We need to ensure there are enough dimensions in the input for the number of
          # full-range axes.
          # If the last index is a newaxis, then ExpandDims will make sure that it makes sense.
          # Otherwise we need to add an additional check.
          if len(new_axes) > 0 and isinstance(val[-1], _NewAxis):
              sliced = (
                  self  # no check needed, ExpandDims will do the trick
              )
          else:
              sliced = nvidia.dali.fn.subscript_dim_check(
                  self, num_subscripts=len(idxs)
              )
      else:
          sliced = nvidia.dali.fn.tensor_subscript(
              self, **slice_args, num_subscripts=len(idxs)
          )
  
      if len(new_axes) == 0:
          return sliced
      else:
          return nvidia.dali.fn.expand_dims(
              sliced, axes=new_axes, new_axis_names=new_axis_names
          )
  
imports: from __future__ import annotations
  import sys
  from ._utils.hacks import not_iterable
label_desc: Use image node_id 31 for calling the DataNode obj's underlying member method code with example usage: obj.__getitem__(val) and returns: False, sliced, nvidia, True, True, True