code: def generate_loop(
      func_inputs,
      func_outputs,
      func_retval,
      ufunc_inputs,
      ufunc_outputs,
  ):
      """
      Generate a UFunc loop function that calls a function given as its
      data parameter with the specified input and output arguments and
      return value.
  
      This function can be passed to PyUFunc_FromFuncAndData.
  
      Parameters
      ----------
      func_inputs, func_outputs, func_retval : str
          Signature of the function to call, given as type codes of the
          input, output and return value arguments. These 1-character
          codes are given according to the CY_TYPES and TYPE_NAMES
          lists above.
  
          The corresponding C function signature to be called is:
  
              retval func(intype1 iv1, intype2 iv2, ..., outtype1 *ov1, ...);
  
          If len(ufunc_outputs) == len(func_outputs)+1, the return value
          is treated as the first output argument. Otherwise, the return
          value is ignored.
  
      ufunc_inputs, ufunc_outputs : str
          Ufunc input and output signature.
  
          This does not have to exactly match the function signature,
          as long as the type casts work out on the C level.
  
      Returns
      -------
      loop_name
          Name of the generated loop function.
      loop_body
          Generated C code for the loop.
  
      """
      if len(func_inputs) != len(ufunc_inputs):
          raise ValueError(
              "Function and ufunc have different number of inputs"
          )
  
      if len(func_outputs) != len(ufunc_outputs) and not (
          func_retval != "v"
          and len(func_outputs) + 1 == len(ufunc_outputs)
      ):
          raise ValueError(
              "Function retval and ufunc outputs don't match"
          )
  
      name = "loop_{}_{}_{}_As_{}_{}".format(
          func_retval,
          func_inputs,
          func_outputs,
          ufunc_inputs,
          ufunc_outputs,
      )
      body = (
          f"cdef void {name}(char **args, np.npy_intp *dims, np.npy_intp *steps, "
          f"void *data) noexcept nogil:\n"
      )
      body += "    cdef np.npy_intp i, n = dims[0]\n"
      body += "    cdef void *func = (<void**>data)[0]\n"
      body += "    cdef char *func_name = <char*>(<void**>data)[1]\n"
  
      for j in range(len(ufunc_inputs)):
          body += "    cdef char *ip%d = args[%d]\n" % (j, j)
      for j in range(len(ufunc_outputs)):
          body += "    cdef char *op%d = args[%d]\n" % (
              j,
              j + len(ufunc_inputs),
          )
  
      ftypes = []
      fvars = []
      outtypecodes = []
      for j in range(len(func_inputs)):
          ftypes.append(CY_TYPES[func_inputs[j]])
          fvars.append(
              "<%s>(<%s*>ip%d)[0]"
              % (CY_TYPES[func_inputs[j]], CY_TYPES[ufunc_inputs[j]], j)
          )
  
      if len(func_outputs) + 1 == len(ufunc_outputs):
          func_joff = 1
          outtypecodes.append(func_retval)
          body += f"    cdef {CY_TYPES[func_retval]} ov0\n"
      else:
          func_joff = 0
  
      for j, outtype in enumerate(func_outputs):
          body += "    cdef %s ov%d\n" % (
              CY_TYPES[outtype],
              j + func_joff,
          )
          ftypes.append("%s *" % CY_TYPES[outtype])
          fvars.append("&ov%d" % (j + func_joff))
          outtypecodes.append(outtype)
  
      body += "    for i in range(n):\n"
      if len(func_outputs) + 1 == len(ufunc_outputs):
          rv = "ov0 = "
      else:
          rv = ""
  
      funcall = (
          "        {}(<{}(*)({}) noexcept nogil>func)({})\n".format(
              rv,
              CY_TYPES[func_retval],
              ", ".join(ftypes),
              ", ".join(fvars),
          )
      )
  
      # Cast-check inputs and call function
      input_checks = []
      for j in range(len(func_inputs)):
          if (ufunc_inputs[j], func_inputs[j]) in DANGEROUS_DOWNCAST:
              chk = "<%s>(<%s*>ip%d)[0] == (<%s*>ip%d)[0]" % (
                  CY_TYPES[func_inputs[j]],
                  CY_TYPES[ufunc_inputs[j]],
                  j,
                  CY_TYPES[ufunc_inputs[j]],
                  j,
              )
              input_checks.append(chk)
  
      if input_checks:
          body += "        if %s:\n" % (" and ".join(input_checks))
          body += "    " + funcall
          body += "        else:\n"
          body += (
              "            sf_error.error(func_name, sf_error.DOMAIN, "
              '"invalid input argument")\n'
          )
          for j, outtype in enumerate(outtypecodes):
              body += "            ov%d = <%s>%s\n" % (
                  j,
                  CY_TYPES[outtype],
                  NAN_VALUE[outtype],
              )
      else:
          body += funcall
  
      # Assign and cast-check output values
      for j, (outtype, fouttype) in enumerate(
          zip(ufunc_outputs, outtypecodes)
      ):
          if (fouttype, outtype) in DANGEROUS_DOWNCAST:
              body += "        if ov%d == <%s>ov%d:\n" % (
                  j,
                  CY_TYPES[outtype],
                  j,
              )
              body += "            (<%s *>op%d)[0] = <%s>ov%d\n" % (
                  CY_TYPES[outtype],
                  j,
                  CY_TYPES[outtype],
                  j,
              )
              body += "        else:\n"
              body += (
                  "            sf_error.error(func_name, sf_error.DOMAIN, "
                  '"invalid output")\n'
              )
              body += "            (<%s *>op%d)[0] = <%s>%s\n" % (
                  CY_TYPES[outtype],
                  j,
                  CY_TYPES[outtype],
                  NAN_VALUE[outtype],
              )
          else:
              body += "        (<%s *>op%d)[0] = <%s>ov%d\n" % (
                  CY_TYPES[outtype],
                  j,
                  CY_TYPES[outtype],
                  j,
              )
      for j in range(len(ufunc_inputs)):
          body += "        ip%d += steps[%d]\n" % (j, j)
      for j in range(len(ufunc_outputs)):
          body += "        op%d += steps[%d]\n" % (
              j,
              j + len(ufunc_inputs),
          )
  
      body += "    sf_error.check_fpe(func_name)\n"
  
      return name, body
  
imports: import itertools
  import json
  import os
  from stat import ST_MTIME
  import argparse
  import re
  import textwrap
  import numpy
label_desc: Use image node_id 3 for calling a global function with example usage: generate_loop(func_inputs, func_outputs, func_retval, ufunc_inputs, ufunc_outputs) and returns: name,  body