class_docstr: A `FunctionGraph` represents a subgraph bound by a set of input variables and
  a set of output variables, ie a subgraph that specifies an PyTensor function.
  The inputs list should contain all the inputs on which the outputs depend.
  `Variable`\s of type `Constant` are not counted as inputs.
  
  The `FunctionGraph` supports the replace operation which allows to replace
  a variable in the subgraph by another, e.g. replace ``(x + x).out`` by
  ``(2 * x).out``. This is the basis for optimization in PyTensor.
  
  This class is also responsible for verifying that a graph is valid
  (ie, all the dtypes and broadcast patterns are compatible with the
  way the `Variable`\s are used) and for tracking the `Variable`\s with
  a :attr:`FunctionGraph.clients` ``dict`` that specifies which `Apply` nodes
  use the `Variable`.  The :attr:`FunctionGraph.clients` field, combined with
  the :attr:`Variable.owner` and each :attr:`Apply.inputs`, allows the graph
  to be traversed in both directions.
  
  It can also be extended with new features using
  :meth:`FunctionGraph.attach_feature`.  See `Feature` for event types and
  documentation.  Extra features allow the `FunctionGraph` to verify new
  properties of a graph as it is optimized.
  
  The constructor creates a `FunctionGraph` which operates on the subgraph
  bound by the inputs and outputs sets.
  
  This class keeps lists for the inputs and outputs and modifies them
  in-place.
docstr: Recursively import everything between an ``Apply`` node and the ``FunctionGraph``'s outputs.
  
  Parameters
  ----------
  apply_node : Apply
      The node to be imported.
  check : bool
      Check that the inputs for the imported nodes are also present in
      the `FunctionGraph`.
  reason : str
      The name of the optimization or operation in progress.
  import_missing : bool
      Add missing inputs instead of raising an exception.
docstr_tok: ["Recursively","import","everything","between","an","``","Apply","``","node","and","the","``","FunctionGraph","``","'s","outputs",".","Parameters","--","--","--","--","--","apply_node",":","Apply","The","node","to","be","imported",".","check",":","bool","Check","that","the","inputs","for","the","imported","nodes","are","also","present","in","the","`","FunctionGraph","`",".","reason",":","str","The","name","of","the","optimization","or","operation","in","progress",".","import_missing",":","bool","Add","missing","inputs","instead","of","raising","an","exception","."]
code: def import_node(
      self,
      apply_node: Apply,
      check: bool = True,
      reason: Optional[str] = None,
      import_missing: bool = False,
  ) -> None:
      """Recursively import everything between an ``Apply`` node and the ``FunctionGraph``'s outputs.
  
      Parameters
      ----------
      apply_node : Apply
          The node to be imported.
      check : bool
          Check that the inputs for the imported nodes are also present in
          the `FunctionGraph`.
      reason : str
          The name of the optimization or operation in progress.
      import_missing : bool
          Add missing inputs instead of raising an exception.
      """
      # We import the nodes in topological order. We only are interested in
      # new nodes, so we use all variables we know of as if they were the
      # input set.  (The functions in the graph module only use the input set
      # to know where to stop going down.)
      new_nodes = io_toposort(self.variables, apply_node.outputs)
  
      if check:
          for node in new_nodes:
              for var in node.inputs:
                  if (
                      var.owner is None
                      and not isinstance(var, AtomicVariable)
                      and var not in self.inputs
                  ):
                      if import_missing:
                          self.add_input(var)
                      else:
                          error_msg = (
                              f"Input {node.inputs.index(var)} ({var})"
                              " of the graph (indices start "
                              f"from 0), used to compute {node}, was not "
                              "provided and not given a value. Use the "
                              "PyTensor flag exception_verbosity='high', "
                              "for more information on this error."
                          )
                          raise MissingInputError(
                              error_msg, variable=var
                          )
  
      for node in new_nodes:
          assert node not in self.apply_nodes
          self.apply_nodes.add(node)
          if not hasattr(node.tag, "imported_by"):
              node.tag.imported_by = []
          node.tag.imported_by.append(str(reason))
          for output in node.outputs:
              self.setup_var(output)
              self.variables.add(output)
          for i, input in enumerate(node.inputs):
              if input not in self.variables:
                  self.setup_var(input)
                  self.variables.add(input)
              self.add_client(input, (node, i))
          self.execute_callbacks("on_import", node, reason)
  
imports: import time
  from collections import OrderedDict
  from collections.abc import Iterable, Sequence
  from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
  import pytensor
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Apply, AtomicVariable, Variable, applys_between, clone_get_equiv, graph_inputs, io_toposort, vars_between
  from pytensor.graph.basic import as_string
  from pytensor.graph.features import AlreadyThere, Feature, ReplaceValidate
  from pytensor.graph.utils import MetaObject, MissingInputError, TestValueError
  from pytensor.misc.ordered_set import OrderedSet
label_desc: Use image node_id 9 for calling the FunctionGraph obj's underlying member method code with example usage: obj.import_node(apply_node, check, reason, import_missing) without return types