class PathHash(object): """ Used for encoding the stacktrace. A stacktrace can be seen by a list of tuples (filename and linenumber): e.g. [(fn1, 25), (fn2, 30)] this is encoded as a string: encoded = 'fn1:25->fn2->30' However, the filename could possibly contain '->', therefore the filename is hashed into a number. So, the encoding becomes: encoded = '0:25->1:30' """ def __init__(self): self._string_hash = StringHash() self._current_path = '' def set_path(self, path): self._current_path = path def get_path(self, fn, ln): """ :param fn: String with the filename :param ln: line number :return: Encoded path name. """ self._current_path = self.append(fn, ln) return self._current_path def append(self, fn, ln): """ Concatenate the current_path with the new path. :param fn: filename :param ln: line number :return: The new current_path """ if self._current_path: return self._current_path + STRING_SPLIT + self._encode(fn, ln) return self._encode(fn, ln) def _encode(self, fn, ln): """ Encoded fn and ln in the following way: _encode(fn, ln) => hash(fn):ln :param fn: filename (string) :param ln: linenumber (int) :return: String with the hashed filename, and linenumber """ return str(self._string_hash.hash(fn)) + LINE_SPLIT + str(ln) def _decode(self, string): """ Opposite of _encode Example: _decode('0:12') => ('fn1', 12) """ hash, ln = string.split(LINE_SPLIT) return self._string_hash.unhash(int(hash)), int(ln) @staticmethod def get_indent(path): """ Compute the amount of callers given a path. :param path: the path from the root to the statement :return: an integer """ if path: return len(path.split(STRING_SPLIT)) return 0 def get_code(self, path): """ :param path: only take the last tuple of the path. the last part contains the code line, but hashed. :return: the line of code, based on the given path """ last = path.rpartition(STRING_SPLIT)[-1] return self._string_hash.unhash(int(last.split(LINE_SPLIT, 1)[1])) def get_last_fn_ln(self, string): last = string.rpartition(STRING_SPLIT)[-1] return self._decode(last) def get_stacklines_path(self, stack_lines, index): """ Used for grouping multiple requests :param stack_lines: list of StackLine objects. :param index: index in the stack_lines, so 0 <= index < len(stack_lines) :return: the StackLinePath that belongs to the given index """ self.set_path('') path = [] while index >= 0: path.append(stack_lines[index].code) current_indent = stack_lines[index].indent while index >= 0 and stack_lines[ index].indent != current_indent - 1: index -= 1 for code_line in reversed(path): self._current_path = self.append( code_line.filename, self._string_hash.hash(code_line.code)) return self._current_path
def test_stringhash(self): string_hash = StringHash() self.assertEqual(string_hash.hash('abc'), 0) self.assertEqual(string_hash.hash('def'), 1) self.assertEqual(string_hash.hash('abc'), 0)
def __init__(self): self._string_hash = StringHash() self._current_path = ''
def test_unhash(self): string_hash = StringHash() self.assertEqual(string_hash.unhash(string_hash.hash('abc')), 'abc') self.assertRaises(ValueError, string_hash.unhash, 'unknown')