class_docstr: Base class for tracked locals and stack values
  
  VariableTracker instances are immutable and should be copied in
  order to change them.
class_docstr_tok: ["Base","class","for","tracked","locals","and","stack","values","VariableTracker","instances","are","immutable","and","should","be","copied","in","order","to","change","them","."]
docstr: Walk this object and call fn on all the VariableTracker
  instances
docstr_tok: ["Walk","this","object","and","call","fn","on","all","the","VariableTracker","instances"]
code: def apply(
      cls,
      fn: Callable[["VariableTracker"], "VariableTracker"],
      value,
      cache=None,
      skip_fn=lambda _: False,  # Whether we should skip applying to this var
  ):
      """
      Walk this object and call fn on all the VariableTracker
      instances
      """
      if cache is None:
          cache = dict()
  
      idx = id(value)
      if idx in cache:
          return cache[idx][0]
  
      if isinstance(value, VariableTracker):
          if not skip_fn(value):
  
              def update_object_dict(v):
                  changed = False
                  rv = v.__dict__
                  for key in rv.keys():
                      if key not in v._nonvar_fields:
                          prior = rv[key]
                          rv[key] = cls.apply(fn, prior, cache, skip_fn)
                          changed = changed or prior is not rv[key]
  
                  return v
  
              value = value.unwrap()
              was_realized = value.is_realized()
              result = fn(update_object_dict(value))
              if not was_realized and value.is_realized():
                  # running fn() resulted in value getting realized,
                  # which means we missed updating the contents of result
                  result = update_object_dict(result.unwrap())
          else:
              result = fn(value)
              if result is not None:
                  result = result.unwrap()
      elif istype(value, list):
          result = [cls.apply(fn, v, cache, skip_fn) for v in value]
      elif istype(value, tuple):
          result = tuple(
              cls.apply(fn, v, cache, skip_fn) for v in value
          )
      elif istype(value, (dict, collections.OrderedDict)):
          assert (
              "__name__" not in value
          ), "_nonvar_fields should have excluded this"
          result = {
              k: cls.apply(fn, v, cache, skip_fn)
              for k, v in list(value.items())
          }
      else:
          result = value
  
      # save `value` to keep it alive and ensure id() isn't reused
      cache[idx] = (result, value)
      return result
  
imports: import collections
  from enum import Enum
  from typing import Any, Callable, Dict, List
  from ..None import variables
  from ..current_scope_id import current_scope_id
  from ..exc import unimplemented
  from ..source import AttrSource, Source
  from ..utils import identity, istype
label_desc: Use image node_id 3 for calling the VariableTracker obj's underlying member method code with example usage: obj.apply(cls, fn, value, cache, skip_fn) and returns: result, cache, v