code: def _validate_normalize_axes(
      axes, axis, keepdims, input_coredimss, output_coredimss
  ):
      """
      Validates logic of `axes`/`axis`/`keepdims` arguments and normalize them.
      Refer to [1]_ for details
  
      Arguments
      ---------
      axes: List of tuples
      axis: int
      keepdims: bool
      input_coredimss: List of Tuple of dims
      output_coredimss: List of Tuple of dims
  
      Returns
      -------
      input_axes: List of tuple of int
      output_axes: List of tuple of int
  
      References
      ----------
      .. [1] https://docs.scipy.org/doc/numpy/reference/ufuncs.html#optional-keyword-arguments
      """
      nin = len(input_coredimss)
      nout = (
          1
          if not isinstance(output_coredimss, list)
          else len(output_coredimss)
      )
  
      if axes is not None and axis is not None:
          raise ValueError(
              "Only one of `axis` or `axes` keyword arguments should be given"
          )
      if axes and not isinstance(axes, list):
          raise ValueError("`axes` has to be of type list")
  
      output_coredimss = (
          output_coredimss if nout > 1 else [output_coredimss]
      )
      filtered_core_dims = list(filter(len, input_coredimss))
      nr_outputs_with_coredims = len(
          [True for x in output_coredimss if len(x) > 0]
      )
  
      if keepdims:
          if nr_outputs_with_coredims > 0:
              raise ValueError(
                  "`keepdims` can only be used for scalar outputs"
              )
          output_coredimss = len(output_coredimss) * [
              filtered_core_dims[0]
          ]
  
      core_dims = input_coredimss + output_coredimss
      if axis is not None:
          if not isinstance(axis, int):
              raise ValueError(
                  "`axis` argument has to be an integer value"
              )
          if filtered_core_dims:
              cd0 = filtered_core_dims[0]
              if len(cd0) != 1:
                  raise ValueError(
                      "`axis` can be used only, if one core dimension is present"
                  )
              for cd in filtered_core_dims:
                  if cd0 != cd:
                      raise ValueError(
                          "To use `axis`, all core dimensions have to be equal"
                      )
  
      # Expand dafaults or axis
      if axes is None:
          if axis is not None:
              axes = [(axis,) if cd else tuple() for cd in core_dims]
          else:
              axes = [tuple(range(-len(icd), 0)) for icd in core_dims]
      elif not isinstance(axes, list):
          raise ValueError("`axes` argument has to be a list")
      axes = [(a,) if isinstance(a, int) else a for a in axes]
  
      if (
          (nr_outputs_with_coredims == 0)
          and (nin != len(axes))
          and (nin + nout != len(axes))
      ) or (
          (nr_outputs_with_coredims > 0) and (nin + nout != len(axes))
      ):
          raise ValueError(
              "The number of `axes` entries is not equal the number of input and output arguments"
          )
  
      # Treat outputs
      output_axes = axes[nin:]
      output_axes = (
          output_axes
          if output_axes
          else [tuple(range(-len(ocd), 0)) for ocd in output_coredimss]
      )
      input_axes = axes[:nin]
  
      # Assert we have as many axes as output core dimensions
      for idx, (iax, icd) in enumerate(
          zip(input_axes, input_coredimss)
      ):
          if len(iax) != len(icd):
              raise ValueError(
                  "The number of `axes` entries for argument #{} is not equal "
                  "the number of respective input core dimensions in signature".format(
                      idx
                  )
              )
      if not keepdims:
          for idx, (oax, ocd) in enumerate(
              zip(output_axes, output_coredimss)
          ):
              if len(oax) != len(ocd):
                  raise ValueError(
                      "The number of `axes` entries for argument #{} is not equal "
                      "the number of respective output core dimensions in signature".format(
                          idx
                      )
                  )
      else:
          if input_coredimss:
              icd0 = input_coredimss[0]
              for icd in input_coredimss:
                  if icd0 != icd:
                      raise ValueError(
                          "To use `keepdims`, all core dimensions have to be equal"
                      )
              iax0 = input_axes[0]
              output_axes = [iax0 for _ in output_coredimss]
  
      return input_axes, output_axes
  
file_path: dask/dask/array/gufunc.py
imports: from __future__ import annotations
  import re
  import numpy
  from tlz import concat, merge, unique
  from dask.array.core import Array, apply_infer_dtype, asarray, blockwise, getitem
  from dask.array.utils import meta_from_array
  from dask.core import flatten
  from dask.highlevelgraph import HighLevelGraph
label_desc: Use image node_id 2 for calling a global function with example usage: _validate_normalize_axes(axes, axis, keepdims, input_coredimss, output_coredimss) and returns: input_axes,  output_axes