def substr_surrounded_by_chars(full_str, char_pair, offset=0):
    """
    Extract substring surrounded by open & close chars.
    For example, 'value: { k1: v1, k2: v2 }'
    :param full_str: target string.
    :param char_pair: a tuple contains open & close tag.
    :param offset: Start point to search substring.
    :return: Substring which include open & close char.
    """
    open_tag, close_tag = char_pair
    if len(full_str) <= offset or len(full_str) < 2:
        raise ValueError('Invalid offset value.')
    if len(open_tag) != 1 and len(close_tag) != 1:
        raise ValueError('Invalid close tag characters, open & close tag length must be 1.')
    stack = []
    open_index = -1
    close_index = -1
    for i in range(offset, len(full_str)):
        ch = full_str[i]
        if ch == open_tag:
            if open_index == -1:
                open_index = i
            stack.append(ch)
        elif ch == close_tag:
            if len(stack) < 1:
                raise ValueError("Invalid str value, close tag before open tag")
            stack.pop()
            if len(stack) == 0:
                close_index = i
                break
    if len(stack) > 0:
        raise ValueError('Invalid str value, open tag and close tag are not paired.')
    return full_str[open_index:close_index + 1]