def test_api(self): raw_text = 'abc- "d" ""ef"" g' stream = StringStream(raw_text) assert stream.index == 0 assert stream.len == len(raw_text) buf = stream.read(1) assert_match(buf, "a") assert stream.index == 1 stream.seek(-1) assert stream.index == 0 buf = stream.advance_past_chars(['"']) buf = stream.advance_past_string_with_gdb_escapes() assert_match(buf, "d") buf = stream.advance_past_chars(['"']) buf = stream.advance_past_chars(['"']) buf = stream.advance_past_string_with_gdb_escapes() assert_match(buf, "ef") # read way past end to test it gracefully returns the # remainder of the string without failing buf = stream.read(50) assert_match(buf, '" g')
def _parse_dict(stream: StringStream): """Parse dictionary, with optional starting character '{' return (tuple): Number of characters parsed from to_parse Parsed dictionary """ obj: Dict[str, Union[str, list]] = {} logger.debug("%s", fmt_green("parsing dict")) while True: c = stream.read(1) if c in _WHITESPACE: pass elif c in ["{", ","]: pass elif c in ["}", ""]: # end of object, exit loop break else: stream.seek(-1) key, val = _parse_key_val(stream) if key in obj: # This is a gdb bug. We should never get repeated keys in a dict! # See https://sourceware.org/bugzilla/show_bug.cgi?id=22217 # and https://github.com/cs01/pygdbmi/issues/19 # Example: # thread-ids={thread-id="1",thread-id="2"} # Results in: # thread-ids: {{'thread-id': ['1', '2']}} # Rather than the lossy # thread-ids: {'thread-id': 2} # '1' got overwritten! if isinstance(obj[key], list): obj[key].append(val) # type: ignore else: obj[key] = [obj[key], val] else: obj[key] = val look_ahead_for_garbage = True c = stream.read(1) while look_ahead_for_garbage: if c in ["}", ",", ""]: look_ahead_for_garbage = False else: # got some garbage text, skip it. for example: # name="gdb"gargage # skip over 'garbage' # name="gdb"\n # skip over '\n' logger.debug("skipping unexpected charcter: " + c) c = stream.read(1) stream.seek(-1) logger.debug("parsed dict") logger.debug("%s", fmt_green(obj)) return obj
def _parse_array(stream: StringStream): """Parse an array, stream should be passed the initial [ returns: Parsed array """ logger.debug("parsing array") arr = [] while True: c = stream.read(1) if c in _GDB_MI_VALUE_START_CHARS: stream.seek(-1) val = _parse_val(stream) arr.append(val) elif c in _WHITESPACE: pass elif c == ",": pass elif c == "]": # Stop when this array has finished. Note # that elements of this array can be also be arrays. break logger.debug("parsed array:") logger.debug("%s", fmt_green(arr)) return arr
def _parse_val(stream: StringStream): """Parse value from string returns: Parsed value (either a string, array, or dict) """ logger.debug("parsing value") while True: c = stream.read(1) if c == "{": # Start object val = _parse_dict(stream) break elif c == "[": # Start of an array val = _parse_array(stream) break elif c == '"': # Start of a string val = stream.advance_past_string_with_gdb_escapes() break elif _DEBUG: raise ValueError("unexpected character: %s" % c) else: logger.warn(f'unexpected character: "{c}" ({ord(c)}). Continuing.') val = "" # this will be overwritten if there are more characters to be read logger.debug("parsed value:") logger.debug("%s", fmt_green(val)) return val