def skip_whitespace(s, start): """Find the first non-whitespace character in a string. :param str s: an input string :param int start: an index where the search starts :return: index of the first non-whitespace character or len(s)""" i = start while i < len(s) and s[i].isspace(): i += 1 return i def str_row_col(s, i): """Translate a string index i to a row and col number. :param str s: an input string :param int i: an index pointing into s :return: a string position as (row, col) :rtype: (int, int)""" k = 0 (r, c) = (0, 0) for (r, line) in enumerate(s.splitlines(True)): c = i-k if k+len(line) > i: break else: k += len(line) return (r+1, c+1) class ParserError(Exception): def __init__(self, msg, s, i): self.msg = msg (self.row, self.col) = str_row_col(s, i) self.context = s[i:i+16] def __str__(self): return "{0} at row {1}, col {2}, got '{3}...' instead".format(self.msg, self.row, self.col, self.context) class ParserWarning(ParserError): pass