code: def fgraph_to_python(
      fgraph: FunctionGraph,
      op_conversion_fn: Callable,
      *,
      type_conversion_fn: Callable = lambda x, **kwargs: x,
      order: Optional[list[Apply]] = None,
      storage_map: Optional["StorageMapType"] = None,
      fgraph_name: str = "fgraph_to_python",
      global_env: Optional[dict[Any, Any]] = None,
      local_env: Optional[dict[Any, Any]] = None,
      get_name_for_object: Callable[[Any], str] = get_name_for_object,
      squeeze_output: bool = False,
      **kwargs,
  ) -> Callable:
      """Convert a `FunctionGraph` into a regular Python function.
  
      Parameters
      ==========
      fgraph
          The `FunctionGraph` to convert.
      op_conversion_fn
          A callable used to convert nodes inside `fgraph` based on their `Op`
          types.  It must have the signature
          ``(op: Op, node: Apply=None, storage_map: Dict[Variable, List[Optional[Any]]]=None, **kwargs)``.
      type_conversion_fn
          A callable used to convert the values in `storage_map`.  It must have
          the signature
          ``(value: Optional[Any], variable: Variable=None, storage: List[Optional[Any]]=None, **kwargs)``.
      order
          The `order` argument to `map_storage`.
      storage_map
          The `storage_map` argument to `map_storage`.
      fgraph_name
          The name used for the resulting function.
      global_env
          The global environment used when the function is constructed.
          The default is an empty ``dict``.
      local_env
          The local environment used when the function is constructed.
          The default is ``locals()``.
      get_name_for_object
          A function used to provide names for the objects referenced within the
          generated function.
      squeeze_output
          If the `FunctionGraph` has only one output and this option is
          ``True``, return the single output instead of a tuple with the output.
      **kwargs
          The remaining keywords are passed to `python_conversion_fn`
      """
  
      if order is None:
          order = fgraph.toposort()
  
      if storage_map is None:
          storage_map = {}
  
      unique_name = unique_name_generator([fgraph_name])
  
      if global_env is None:
          global_env = {}
  
      body_assigns = []
      for node in order:
          compiled_func = op_conversion_fn(
              node.op, node=node, storage_map=storage_map, **kwargs
          )
  
          # Create a local alias with a unique name
          local_compiled_func_name = unique_name(compiled_func)
          global_env[local_compiled_func_name] = compiled_func
  
          node_input_names = []
          for i in node.inputs:
              local_input_name = unique_name(i)
              input_storage = storage_map.setdefault(
                  i, [None if not isinstance(i, Constant) else i.data]
              )
              if input_storage[0] is not None or isinstance(
                  i, Constant
              ):
                  # Constants need to be assigned locally and referenced
                  global_env[local_input_name] = type_conversion_fn(
                      input_storage[0],
                      variable=i,
                      storage=input_storage,
                      **kwargs,
                  )
                  # TODO: We could attempt to use the storage arrays directly
                  # E.g. `local_input_name = f"{local_input_name}[0]"`
              node_input_names.append(local_input_name)
  
          node_output_names = [unique_name(v) for v in node.outputs]
  
          assign_str = f"{', '.join(node_output_names)} = {local_compiled_func_name}({', '.join(node_input_names)})"
          assign_comment_str = f"{indent(str(node), '# ')}"
          assign_block_str = f"{assign_comment_str}\n{assign_str}"
          body_assigns.append(assign_block_str)
  
      # Handle `Constant`-only outputs (these don't have associated `Apply`
      # nodes, so the above isn't applicable)
      for out in fgraph.outputs:
          if isinstance(out, Constant):
              local_output_name = unique_name(out)
              if local_output_name not in global_env:
                  output_storage = storage_map.setdefault(
                      out,
                      [
                          None
                          if not isinstance(out, Constant)
                          else out.data
                      ],
                  )
                  global_env[local_output_name] = type_conversion_fn(
                      output_storage[0],
                      variable=out,
                      storage=output_storage,
                      **kwargs,
                  )
  
      fgraph_input_names = [unique_name(v) for v in fgraph.inputs]
      fgraph_output_names = [unique_name(v) for v in fgraph.outputs]
      joined_body_assigns = indent("\n".join(body_assigns), "    ")
  
      if len(fgraph_output_names) == 1:
          fgraph_return_src = (
              fgraph_output_names[0]
              if squeeze_output
              else f"({fgraph_output_names[0]},)"
          )
      else:
          fgraph_return_src = ", ".join(fgraph_output_names)
  
      fgraph_def_src = dedent(
          f"""
      def {fgraph_name}({", ".join(fgraph_input_names)}):
  {indent(joined_body_assigns, " " * 4)}
          return {fgraph_return_src}
      """
      ).strip()
  
      if local_env is None:
          local_env = locals()
  
      fgraph_def = compile_function_src(
          fgraph_def_src,
          fgraph_name,
          {**globals(), **global_env},
          local_env,
      )
  
      return fgraph_def
  
imports: import builtins
  import io
  import re
  import sys
  import traceback
  import warnings
  from collections import Counter, defaultdict
  from collections.abc import Iterable, Sequence
  from keyword import iskeyword
  from operator import itemgetter
  from tempfile import NamedTemporaryFile
  from textwrap import dedent, indent
  from typing import TYPE_CHECKING, Any, Callable, NoReturn, Optional, TextIO, TypeVar, Union, cast
  import numpy
  from pytensor import config, utils
  from pytensor.graph.basic import Apply, Constant, Variable
  from pytensor.graph.fg import FunctionGraph
label_desc: Use image node_id 10 for calling a global function with example usage: fgraph_to_python(fgraph, op_conversion_fn) and returns: fgraph_def