code: def raise_with_op(node, thunk=None, exc_info=None, storage_map=None):
      """
      Re-raise an exception while annotating the exception object with
      debug info.
  
      Parameters
      ----------
      node : Apply node
          The Apply node object that resulted in the raised exception.
      exc_info : tuple, optional
          A tuple containing the exception type, exception object and
          associated traceback, as would be returned by a call to
          `sys.exc_info()` (which is done if `None` is passed).
      storage_map: dict, optional
          storage map of the theano function that resulted in the
          raised exception.
  
      Notes
      -----
      This re-raises the exception described by `exc_info` (or the last
      one raised, if `exc_info` is omitted) and annotates the exception
      object with several new members which may be helpful for debugging
      Theano graphs. They are:
  
       * __op_instance__: The Op that is responsible for the exception
         being raised.
       * __thunk_trace__: A traceback corresponding to the code that
         actually generated the exception, if it is available.
       * __applynode_index__: The index of the Apply node corresponding
         to this op in `op.fgraph.toposort()`.
  
      The exception is not annotated if it is of type `KeyboardInterrupt`.
  
      """
      if exc_info is None:
          exc_info = sys.exc_info()
      exc_type, exc_value, exc_trace = exc_info
      if exc_type == KeyboardInterrupt:
          # print a simple traceback from KeyboardInterrupt
          reraise(exc_type, exc_value, exc_trace)
      try:
          trace = node.outputs[0].tag.trace
      except AttributeError:
          try:
              trace = node.op.tag.trace
          except AttributeError:
              trace = ()
      exc_value.__thunk_trace__ = trace
      exc_value.__op_instance__ = node
      topo = node.fgraph.toposort()
      if node in topo:
          node_index = topo.index(node)
      else:
          node_index = None
      exc_value.__applynode_index__ = node_index
  
      hints = []
      detailed_err_msg = "\nApply node that caused the error: " + str(
          node
      )
      if exc_value.__applynode_index__ is not None:
          detailed_err_msg += "\nToposort index: %d" % node_index
  
      types = [getattr(ipt, "type", "No type") for ipt in node.inputs]
      detailed_err_msg += "\nInputs types: %s\n" % types
  
      if thunk is not None:
          if hasattr(thunk, "inputs"):
              shapes = [
                  getattr(ipt[0], "shape", "No shapes")
                  for ipt in thunk.inputs
              ]
              strides = [
                  getattr(ipt[0], "strides", "No strides")
                  for ipt in thunk.inputs
              ]
              scalar_values = []
              for ipt in thunk.inputs:
                  if getattr(ipt[0], "size", -1) <= 5:
                      scalar_values.append(ipt[0])
                  else:
                      scalar_values.append("not shown")
          else:
              shapes = "The thunk don't have an inputs attributes."
              strides = (
                  "So we can't access the strides of inputs values"
              )
              scalar_values = "And can't print its inputs scalar value"
          clients = [
              [c[0] for c in var.clients] for var in node.outputs
          ]
          detailed_err_msg += (
              "Inputs shapes: %s" % shapes
              + "\nInputs strides: %s" % strides
              + "\nInputs values: %s" % scalar_values
          )
          if theano.config.exception_verbosity == "high":
              detailed_err_msg += "\nInputs type_num: %s" % str(
                  [
                      getattr(getattr(i[0], "dtype", ""), "num", "")
                      for i in thunk.inputs
                  ]
              )
          if hasattr(node.op, "__input_name__"):
              detailed_err_msg += "\nInputs name: %s\n" % str(
                  node.op.__input_name__
              )
  
          detailed_err_msg += "\nOutputs clients: %s\n" % clients
      else:
          hints.append(
              "HINT: Use another linker then the c linker to"
              " have the inputs shapes and strides printed."
          )
  
      # Print node backtraces
      tr = getattr(node.outputs[0].tag, "trace", [])
      if isinstance(tr, list) and len(tr) > 0:
          detailed_err_msg += "\nBacktrace when the node is created(use Theano flag traceback.limit=N to make it longer):\n"
  
          # Print separate message for each element in the list of batcktraces
          sio = StringIO()
          for subtr in tr:
              traceback.print_list(subtr, sio)
          detailed_err_msg += str(sio.getvalue())
      else:
          hints.append(
              "HINT: Re-running with most Theano optimization disabled could"
              " give you a back-trace of when this node was created. This can"
              " be done with by setting the Theano flag"
              " 'optimizer=fast_compile'. If that does not work,"
              " Theano optimizations can be disabled with 'optimizer=None'."
          )
  
      if theano.config.exception_verbosity == "high":
          f = StringIO()
          theano.printing.debugprint(
              node, file=f, stop_on_name=True, print_type=True
          )
          detailed_err_msg += "\nDebugprint of the apply node: \n"
          detailed_err_msg += f.getvalue()
  
      # Prints output_map
      if (
          theano.config.exception_verbosity == "high"
          and storage_map is not None
      ):
          detailed_err_msg += "\nStorage map footprint:\n"
          shared_input_list = [
              item
              for item in node.fgraph.inputs
              if isinstance(item, theano.compile.SharedVariable)
          ]
          nonshared_input_list = [
              item
              for item in node.fgraph.inputs
              if not isinstance(item, theano.compile.SharedVariable)
          ]
          storage_map_list = []
          total_size = 0
          total_size_inputs = 0
          for k in storage_map:
              storage_map_item = []
  
              # storage_map_item[0]: the variable
              storage_map_item.append(str(k))
  
              # storage_map_item[1]: the shape
              shapeinfo = None
              if hasattr(storage_map[k][0], "shape"):
                  shapeinfo = storage_map[k][0].shape
                  if len(shapeinfo) != 0:
                      storage_map_item.append(shapeinfo)
                  else:
                      storage_map_item.append(tuple())
              else:
                  storage_map_item.append(None)
  
              # storage_map_item[2]: itemsize
              # storage_map_item[3]: bytes
              if hasattr(storage_map[k][0], "dtype"):
                  dtype = storage_map[k][0].dtype
                  storage_map_item.append(np.dtype(dtype).itemsize)
                  if shapeinfo is None:
                      storage_map_item.append(-1)
                  else:
                      sz = np.dtype(dtype).itemsize * np.prod(shapeinfo)
                      storage_map_item.append(sz)
                      total_size += sz
                      if not k.owner:
                          total_size_inputs += sz
                      else:
                          # If it is a view, don't count it twice.
                          if getattr(k.owner.op, "view_map", None):
                              vmap = k.owner.op.view_map
                              out_idx = k.owner.outputs.index(k)
                              data = storage_map[k][0]
                              if out_idx in vmap:
                                  assert len(vmap[out_idx]) == 1
                                  input_data = storage_map[
                                      k.owner.inputs[vmap[out_idx][0]]
                                  ][0]
                                  if k.type.may_share_memory(
                                      data, input_data
                                  ):
                                      total_size -= sz
                          # If it is a destroyed input, the input
                          # shouldn't be in the storage_map anymore
                          # except if there is a special flag used. So
                          # we still must check it.
                          if getattr(k.owner.op, "destroy_map", None):
                              vmap = k.owner.op.destroy_map
                              out_idx = k.owner.outputs.index(k)
                              data = storage_map[k][0]
                              if out_idx in vmap:
                                  assert len(vmap[out_idx]) == 1
                                  input_data = storage_map[
                                      k.owner.inputs[vmap[out_idx][0]]
                                  ][0]
                                  if k.type.may_share_memory(
                                      data, input_data
                                  ):
                                      total_size -= sz
              else:
                  bytes = getsizeof(storage_map[k][0])
                  storage_map_item.append(bytes)
                  storage_map_item.append(-1)
  
              # Flag of shared val
              # storage_map_item[4]
              if k in shared_input_list:
                  storage_map_item.append(True)
              elif k in nonshared_input_list:
                  storage_map_item.append(False)
              else:
                  storage_map_item.append(None)
              storage_map_list.append(storage_map_item)
  
          from operator import itemgetter
  
          storage_map_list.sort(key=itemgetter(3), reverse=True)
          for item in storage_map_list:
              if item[3] == -1:
                  continue
              detailed_err_msg += " - " + item[0] + ", "
              if item[4] is True:
                  detailed_err_msg += "Shared Input, "
              elif item[4] is False:
                  detailed_err_msg += "Input, "
              if item[1] is not None:
                  detailed_err_msg += "Shape: %s, " % str(item[1])
              detailed_err_msg += "ElemSize: %s Byte(s)" % item[2]
              if item[3] is not None:
                  detailed_err_msg += (
                      ", TotalSize: %s Byte(s)\n" % item[3]
                  )
              else:
                  detailed_err_msg += "\n"
          detailed_err_msg += " TotalSize: %s Byte(s) %.3f GB\n" % (
              total_size,
              total_size / 1024.0 / 1024 / 1024,
          )
          detailed_err_msg += (
              " TotalSize inputs: %s Byte(s) %.3f GB\n"
              % (
                  total_size_inputs,
                  total_size_inputs / 1024.0 / 1024 / 1024,
              )
          )
  
      else:
          hints.append(
              "HINT: Use the Theano flag 'exception_verbosity=high'"
              " for a debugprint and storage map footprint of this apply node."
          )
  
      try:
          exc_value = exc_type(
              str(exc_value)
              + detailed_err_msg
              + "\n"
              + "\n".join(hints)
          )
      except TypeError:
          print(
              "WARNING: %s error does not allow us to add extra error message"
              % str(exc_type)
          )
          # Some exception need extra parameter in inputs. So forget the
          # extra long error message in that case.
          pass
      reraise(exc_type, exc_value, exc_trace)
  
imports: from __future__ import absolute_import, print_function, division
  from copy import copy, deepcopy
  from sys import getsizeof
  import sys
  import traceback
  import numpy
  import theano
  from theano.compat import izip
  from six import reraise
  from six.moves import StringIO
  from theano.gof import utils
  from theano.gof import graph
  from theano.gof.type import Type
  from .utils import undef
label_desc: Use image node_id 3 for calling a global function with example usage: raise_with_op(node, thunk, exc_info, storage_map) without return types