]>
Commit | Line | Data |
---|---|---|
25ad2cf6 VSO |
1 | #!/usr/bin/env python3 |
2 | # | |
3 | # Intended usage: | |
4 | # | |
5 | # git grep -l '\.qmp(' | xargs ./scripts/python_qmp_updater.py | |
6 | # | |
7 | ||
8 | import re | |
9 | import sys | |
10 | from typing import Optional | |
11 | ||
12 | start_reg = re.compile(r'^(?P<padding> *)(?P<res>\w+) = (?P<vm>.*).qmp\(', | |
13 | flags=re.MULTILINE) | |
14 | ||
15 | success_reg_templ = re.sub('\n *', '', r""" | |
16 | (\n*{padding}(?P<comment>\#.*$))? | |
17 | \n*{padding} | |
18 | ( | |
19 | self.assert_qmp\({res},\ 'return',\ {{}}\) | |
20 | | | |
21 | assert\ {res}\['return'\]\ ==\ {{}} | |
22 | | | |
23 | assert\ {res}\ ==\ {{'return':\ {{}}}} | |
24 | | | |
25 | self.assertEqual\({res}\['return'\],\ {{}}\) | |
26 | )""") | |
27 | ||
28 | some_check_templ = re.sub('\n *', '', r""" | |
29 | (\n*{padding}(?P<comment>\#.*$))? | |
30 | \s*self.assert_qmp\({res},""") | |
31 | ||
32 | ||
33 | def tmatch(template: str, text: str, | |
34 | padding: str, res: str) -> Optional[re.Match[str]]: | |
35 | return re.match(template.format(padding=padding, res=res), text, | |
36 | flags=re.MULTILINE) | |
37 | ||
38 | ||
39 | def find_closing_brace(text: str, start: int) -> int: | |
40 | """ | |
41 | Having '(' at text[start] search for pairing ')' and return its index. | |
42 | """ | |
43 | assert text[start] == '(' | |
44 | ||
45 | height = 1 | |
46 | ||
47 | for i in range(start + 1, len(text)): | |
48 | if text[i] == '(': | |
49 | height += 1 | |
50 | elif text[i] == ')': | |
51 | height -= 1 | |
52 | if height == 0: | |
53 | return i | |
54 | ||
55 | raise ValueError | |
56 | ||
57 | ||
58 | def update(text: str) -> str: | |
59 | result = '' | |
60 | ||
61 | while True: | |
62 | m = start_reg.search(text) | |
63 | if m is None: | |
64 | result += text | |
65 | break | |
66 | ||
67 | result += text[:m.start()] | |
68 | ||
69 | args_ind = m.end() | |
70 | args_end = find_closing_brace(text, args_ind - 1) | |
71 | ||
72 | all_args = text[args_ind:args_end].split(',', 1) | |
73 | ||
74 | name = all_args[0] | |
75 | args = None if len(all_args) == 1 else all_args[1] | |
76 | ||
77 | unchanged_call = text[m.start():args_end+1] | |
78 | text = text[args_end+1:] | |
79 | ||
80 | padding, res, vm = m.group('padding', 'res', 'vm') | |
81 | ||
82 | m = tmatch(success_reg_templ, text, padding, res) | |
83 | ||
84 | if m is None: | |
85 | result += unchanged_call | |
86 | ||
87 | if ('query-' not in name and | |
88 | 'x-debug-block-dirty-bitmap-sha256' not in name and | |
89 | not tmatch(some_check_templ, text, padding, res)): | |
90 | print(unchanged_call + text[:200] + '...\n\n') | |
91 | ||
92 | continue | |
93 | ||
94 | if m.group('comment'): | |
95 | result += f'{padding}{m.group("comment")}\n' | |
96 | ||
97 | result += f'{padding}{vm}.cmd({name}' | |
98 | ||
99 | if args: | |
100 | result += ',' | |
101 | ||
102 | if '\n' in args: | |
103 | m_args = re.search('(?P<pad> *).*$', args) | |
104 | assert m_args is not None | |
105 | ||
106 | cur_padding = len(m_args.group('pad')) | |
107 | expected = len(f'{padding}{res} = {vm}.qmp(') | |
108 | drop = len(f'{res} = ') | |
109 | if cur_padding == expected - 1: | |
110 | # tolerate this bad style | |
111 | drop -= 1 | |
112 | elif cur_padding < expected - 1: | |
113 | # assume nothing to do | |
114 | drop = 0 | |
115 | ||
116 | if drop: | |
117 | args = re.sub('\n' + ' ' * drop, '\n', args) | |
118 | ||
119 | result += args | |
120 | ||
121 | result += ')' | |
122 | ||
123 | text = text[m.end():] | |
124 | ||
125 | return result | |
126 | ||
127 | ||
128 | for fname in sys.argv[1:]: | |
129 | print(fname) | |
130 | with open(fname) as f: | |
131 | t = f.read() | |
132 | ||
133 | t = update(t) | |
134 | ||
135 | with open(fname, 'w') as f: | |
136 | f.write(t) |