docs: def getBindings(varList, startLevel=0):
      """Given a list of identifiers as strings, construct the dictonary of the identifiers as keys and the evaluated values of
      the ientifiers as the values. Ie something like {id:full_lookup(id) for id in identifiers}. full_lookup here is looking
      up the value of an identifier by looking at all the bindings at every level in the call stack.
      Return (bindings,unbound) where the bindings is the dictionary of found bindings and the set of unbound identifiers."""
      varsToFind = set(varList)
      bindings = {}
      
      # We start at the level of the caller of getBindings
      frame = inspect.currentframe()
      try:
          for i in xrange(startLevel+1):
              frame = frame.f_back
      except:
          raise Exception("bindings: startLevel {} is too high\n".format(startLevel))
      
      # while we have a current frame look through it for our identifiers
      while frame:
          frameLocals = frame.f_locals
          localKeys = set(varsToFind).intersection(frameLocals)
          for v in localKeys:
              bindings[v]=frameLocals[v]
          varsToFind -= localKeys
          if not varsToFind:
              return (bindings,varsToFind)
  
          frameGlobals = frame.f_globals
          globalKeys = set(varsToFind).intersection(frameGlobals)
          for v in globalKeys:
              bindings[v]=frameGlobals[v]
          varsToFind -= localKeys
          if not varsToFind:
              return (bindings,varsToFind)
  
          frame = frame.f_back
  
      return (bindings,varsToFind)