userver: /data/code/userver/testsuite/pytest_plugins/pytest_userver/utils/tskv.py Source File
Loading...
Searching...
No Matches
tskv.py
1import re
2from typing import TypeAlias
3
4TskvRow: TypeAlias = dict[str, str]
5
6
7_EQUALS_OR_BACKSLASH = re.compile('=|\\\\')
8_UNESCAPED_CHARS = {
9 'n': '\n',
10 'r': '\r',
11 't': '\t',
12 '0': '\0',
13 '\\': '\\',
14 '=': '=',
15}
16
17
18def _search_equals_or_backslash(s: str, start: int) -> int:
19 match = _EQUALS_OR_BACKSLASH.search(s, start)
20
21 return match.start() if match else -1
22
23
24def _search_backslash(s: str, start: int) -> int:
25 return s.find('\\', start)
26
27
28def _parse_pair(pair: str) -> tuple[str, str]:
29 search_fn = _search_equals_or_backslash
30 key = None
31 unescaped_part = ''
32
33 start = 0
34 end = search_fn(pair, start)
35 while end != -1:
36 unescaped_part += pair[start:end]
37 if pair[end] == '=':
38 key = unescaped_part
39 unescaped_part = ''
40 search_fn = _search_backslash
41 elif end + 1 != len(pair) and pair[end + 1] in _UNESCAPED_CHARS:
42 unescaped_part += _UNESCAPED_CHARS[pair[end + 1]]
43 end += 1
44 else:
45 unescaped_part += '\\'
46
47 start = end + 1
48 end = search_fn(pair, start)
49
50 unescaped_part += pair[start:]
51
52 if key is None:
53 raise RuntimeError(f'Invalid tskv pair: {pair}')
54
55 return key, unescaped_part
56
57
58def parse_line(line: str) -> TskvRow:
59 parts = line.rstrip('\n').split('\t')
60 if parts[:1] != ['tskv']:
61 raise RuntimeError(f'Invalid tskv line: {line!r}')
62 return dict(map(_parse_pair, parts[1:]))