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.

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)