Look up the value of an object in a traceback by a dot-lookup string.
    ie. "self.crashreporter.application_name"

    Returns ValueError if value was not found in the scope of the traceback.

    :param tb: traceback
    :param s: lookup string
    :return: value of the

def string_variable_lookup(tb, s):
    """
    Look up the value of an object in a traceback by a dot-lookup string.
    ie. "self.crashreporter.application_name"

    Returns ValueError if value was not found in the scope of the traceback.

    :param tb: traceback
    :param s: lookup string
    :return: value of the
    """

    refs = []
    dot_refs = s.split('.')
    DOT_LOOKUP = 0
    DICT_LOOKUP = 1
    for ii, ref in enumerate(dot_refs):
        dict_refs = dict_lookup_regex.findall(ref)
        if dict_refs:
            bracket = ref.index('[')
            refs.append((DOT_LOOKUP, ref[:bracket]))
            refs.extend([(DICT_LOOKUP, t) for t in dict_refs])
        else:
            refs.append((DOT_LOOKUP, ref))

    scope = tb.tb_frame.f_locals.get(refs[0][1], ValueError)
    if scope is ValueError:
        return scope
    for lookup, ref in refs[1:]:
        try:
            if lookup == DOT_LOOKUP:
                scope = getattr(scope, ref, ValueError)
            else:
                scope = scope.get(ref, ValueError)
        except Exception as e:
            logging.error(e)
            scope = ValueError

        if scope is ValueError:
            return scope
        elif isinstance(scope, (FunctionType, MethodType, ModuleType, BuiltinMethodType, BuiltinFunctionType)):
            return ValueError
    return scope