OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Print a diff generated by generate_idl_diff.py. | |
7 Before printing, sort the idl data of the diff in alphabetical order or by | |
8 diffing tags. | |
9 Usage: print_idl_diff.py diff_file.json order | |
10 diff.json: | |
11 Output of generate_idl_diff.py. A json file consists of a dictionary | |
12 expressing a diff between two defferent Chrome versions.The structure | |
Yuki
2015/09/25 07:12:37
nit: a space just before "The structure"
shimadaa
2015/09/25 07:40:07
Done.
| |
13 of the dictionary is like below. | |
14 order: | |
15 Specify how to sort. Either by using diffing tags or in alphabetical | |
16 order. | |
17 """ | |
18 | |
19 from collections import OrderedDict | |
20 import json | |
21 import sys | |
22 | |
23 from generate_idl_diff import load_json_file | |
24 from generate_idl_diff import EXTATTRIBUTES_AND_MEMBER_TYPES | |
25 from generate_idl_diff import DIFF_TAG | |
26 from generate_idl_diff import DIFF_TAG_ADDED | |
27 from generate_idl_diff import DIFF_TAG_DELETED | |
28 | |
29 | |
30 """Refer to the explanation of generate_idl_diff.py's input files. | |
31 The deffference between input structure of generate_idl_diff.py and | |
32 that of print_diff.py is whether diffing tags are included or not. | |
33 The diffing tags are included in a parts that have a diff. | |
34 {'Interface': { | |
35 'diff_tag': 'deleted' | |
36 'ExtAttributes': [{'Name': '...' | |
37 'diff_tag': 'deleted'}, | |
38 ..., | |
39 ], | |
40 'Consts': [{'Type': '...', | |
41 'Name': '...', | |
42 'Value': '...' | |
43 'diff_tag': 'deleted'}, | |
44 ..., | |
45 ], | |
46 'Attributes': [{'Type': '...', | |
47 'Name': '...', | |
48 'ExtAttributes':[{'Name': '...'}, | |
49 ..., | |
50 ] | |
51 'diff_tag': 'deleted'}, | |
52 ..., | |
53 ], | |
54 'Operations': [{'Type': '...', | |
55 'Name': '...', | |
56 'ExtAttributes':[{'Name': '...'}, | |
57 ..., | |
58 ], | |
59 'Arguments': [{'Type': '...', | |
60 'Name': '...'}, | |
61 ..., | |
62 ] | |
63 'diff_tag': 'deleted'}, | |
64 ..., | |
65 ], | |
66 'Name': '...' | |
67 }, | |
68 { | |
69 'ExtAttributes': [{'Name': '...'}, | |
70 ..., | |
71 ], | |
72 'Consts': [{'Type': '...', | |
73 'Name': '...', | |
74 'Value': '...' | |
75 'diff_tag': 'added'}, | |
76 ..., | |
77 ], | |
78 'Attributes': [{'Type': '...', | |
79 'Name': '...', | |
80 'ExtAttributes':[{'Name': '...'}, | |
81 ..., | |
82 ]}, | |
83 ..., | |
84 ], | |
85 'Operations': [{'Type': '...', | |
86 'Name': '...', | |
87 'ExtAttributes':[{'Name': '...'}, | |
88 ..., | |
89 ], | |
90 'Arguments': [{'Type': '...', | |
91 'Name': '...'}, | |
92 ..., | |
93 ] | |
94 'diff_tag': 'deleted'}, | |
95 ..., | |
96 ] | |
Yuki
2015/09/25 07:12:37
'Name': '...' should be here, too?
shimadaa
2015/09/25 07:40:07
Done.
| |
97 }, | |
98 ..., | |
99 } | |
100 """ | |
101 | |
102 | |
103 class Colorize(object): | |
104 """This class decorates text with colors and outputs it to sys.stdout. | |
105 TODO(shimadaa): This class doesn't work on Windows. Provides a way to | |
106 suppress escape sequences. | |
107 """ | |
108 | |
109 BLACK = 30 | |
110 RED = 31 | |
111 GREEN = 32 | |
112 YELLOW = 33 | |
113 COLORS = (BLACK, RED, GREEN, YELLOW) | |
114 | |
115 def __init__(self, out): | |
116 self.out = out | |
117 | |
118 def reset_color(self): | |
119 """Reset text's color to default. | |
120 """ | |
121 self.out.write('\033[0m') | |
122 | |
123 def change_color(self, color): | |
124 """Change text's color by specifing arguments. | |
125 Args: | |
126 color: A color to change. It should be one of |COLORS|. | |
127 """ | |
128 if color in self.COLORS: | |
129 self.out.write('\033[' + str(color) + 'm') | |
130 else: | |
131 raise Exception('Unsupported color.') | |
132 | |
133 def writeln(self, string): | |
134 """Print text to use for line breaks. | |
135 """ | |
136 self.out.write(string + '\n') | |
137 | |
138 def write(self, string): | |
139 """Print text not to use for line breaks. | |
140 """ | |
141 self.out.write(string) | |
142 | |
143 | |
144 def sort_member_types(interface): | |
145 """Sort the members in the order of EXTATTRIBUTES_AND_MEMBER_TYPES. | |
146 Args: | |
147 interface: An "interface" object | |
148 Returns: | |
149 A sorted "interface" object | |
150 """ | |
151 sorted_interface = OrderedDict() | |
152 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
153 sorted_interface[member_type] = interface.get(member_type) | |
154 sorted_interface[DIFF_TAG] = interface.get(DIFF_TAG) | |
155 return sorted_interface | |
156 | |
157 | |
158 def group_by_tag(interface_or_member_list): | |
159 """Group members of a given argument (an interface or a list of members) by | |
160 tags. | |
161 Args: | |
162 interface_or_member_list: Interface name's list or "members" object's | |
163 list. | |
164 Returns: | |
165 removed: A list that consists of removed ones. | |
Yuki
2015/09/25 07:12:37
I'd recommend to explicitly say that the return va
shimadaa
2015/09/25 07:40:07
Done.
| |
166 added: A list that consists of added ones. | |
167 unspecified: A list that consists of neither removed ones nor added | |
168 ones. | |
169 """ | |
170 removed = [] | |
171 added = [] | |
172 unspecified = [] | |
173 for interface_or_member in interface_or_member_list: | |
174 if DIFF_TAG in interface_or_member: | |
175 if interface_or_member[DIFF_TAG] == DIFF_TAG_DELETED: | |
176 removed.append(interface_or_member) | |
177 elif interface_or_member[DIFF_TAG] == DIFF_TAG_ADDED: | |
178 added.append(interface_or_member) | |
179 else: | |
180 unspecified.append(interface_or_member) | |
181 return (removed, added, unspecified) | |
182 | |
183 | |
184 def sort_interface_names_by_tags(interfaces): | |
185 """Sort the order of interface names like below. | |
186 [a interface name of "interface" deleted whole | |
187 -> a interface name of "interface" added whole | |
188 -> a interface name of "interface" changed part] | |
189 Args: | |
190 interfaces: An "interface" objects. | |
191 Returns: | |
192 A list consists of interface names sorted | |
193 """ | |
194 interface_list = interfaces.values() | |
195 removed, added, unspecified = group_by_tag(interface_list) | |
196 removed = map(lambda interface: interface['Name'], removed) | |
197 added = map(lambda interface: interface['Name'], added) | |
198 unspecified = map(lambda interface: interface['Name'], unspecified) | |
199 sorted_interface_name = removed + added + unspecified | |
200 return sorted_interface_name | |
201 | |
202 | |
203 def sort_members_by_tags(interface): | |
204 """Sort a "members" object by using diff_tag. | |
205 Args: | |
206 An "interface" object | |
207 Returns: | |
208 A sorted "interface" object | |
209 """ | |
210 sorted_interface = OrderedDict() | |
211 if DIFF_TAG in interface: | |
212 return interface | |
213 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
214 member_list = interface[member_type] | |
215 removed, added, unspecified = group_by_tag(member_list) | |
216 sorted_interface[member_type] = removed + added + unspecified | |
217 return sorted_interface | |
218 | |
219 | |
220 def sort_diff_by_tags(interfaces): | |
221 """Sort an "interfaces" object expressing a diff by using diff_tag. | |
222 Args: | |
223 An "interfaces" object loaded by load_json_data(). | |
224 Returns: | |
225 A sorted "interfaces" object | |
226 """ | |
227 sorted_interfaces = OrderedDict() | |
228 sorted_interface_names = sort_interface_names_by_tags(interfaces) | |
229 for interface_name in sorted_interface_names: | |
230 interface = sort_members_by_tags(interfaces[interface_name]) | |
231 sorted_interfaces[interface_name] = sort_member_types(interface) | |
232 return sorted_interfaces | |
233 | |
234 | |
235 def sort_members_in_alphabetical_order(interface): | |
236 """Sort a "members" object in alphabetical order. | |
237 Args: | |
238 An "interface" object | |
239 Returns: | |
240 A sorted "interface" object | |
241 """ | |
242 sorted_interface = OrderedDict() | |
243 for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: | |
244 sorted_members = sorted(interface[member_type], | |
245 key=lambda member: member['Name']) | |
246 sorted_interface[member_type] = sorted_members | |
247 return sorted_interface | |
248 | |
249 | |
250 def sort_diff_in_alphabetical_order(interfaces): | |
251 """Sort diff in alphabetical order. | |
252 Args: | |
253 An "interfaces" object. | |
254 Returns: | |
255 A sorted "interfaces" object in alphabetical order | |
256 """ | |
257 sorted_interfaces = OrderedDict() | |
258 for interface_name in sorted(interfaces.keys()): | |
259 interface = interfaces[interface_name] | |
260 sorted_interface = sort_members_in_alphabetical_order(interface) | |
261 sorted_interface[DIFF_TAG] = interface.get(DIFF_TAG) | |
262 sorted_interfaces[interface_name] = sorted_interface | |
263 return sorted_interfaces | |
264 | |
265 | |
266 def change_color_based_on_tag(member, out): | |
267 """Change color based on diff_tag and output prefix ('+' or '-') if |member| | |
268 is added/deleted. | |
269 Args: | |
270 member: A "member" object | |
271 """ | |
272 if DIFF_TAG in member: | |
273 if member[DIFF_TAG] == DIFF_TAG_DELETED: | |
274 out.change_color(Colorize.RED) | |
275 out.write('- ') | |
276 elif member[DIFF_TAG] == DIFF_TAG_ADDED: | |
277 out.change_color(Colorize.GREEN) | |
278 out.write('+ ') | |
279 else: | |
280 out.change_color(Colorize.BLACK) | |
281 out.write(' ') | |
282 | |
283 | |
284 def print_extattributes(extattributes, out): | |
285 """Print extattributes in an "interface" object. | |
286 Args: | |
287 A list included as a value of "member" object named "ExtAttributes" | |
288 """ | |
289 for extattribute in extattributes: | |
290 out.write(' ') | |
291 change_color_based_on_tag(extattribute, out) | |
292 out.writeln(extattribute['Name']) | |
293 | |
294 | |
295 def print_consts(consts, out): | |
296 """Print consts in an "interface" object. | |
297 Args: | |
298 A list included as a value of "member" object named "Consts" | |
299 """ | |
300 for const in consts: | |
301 out.write(' ') | |
302 change_color_based_on_tag(const, out) | |
303 out.write(const['Type']) | |
304 out.write(' ') | |
305 out.write(const['Name']) | |
306 out.write(' ') | |
307 out.writeln(const['Value']) | |
308 | |
309 | |
310 def print_items(items, callback, out): | |
311 """Print ', ' in ExtAttributes or Arguments. | |
bashi
2015/09/25 07:52:46
How about:
"""Calls |callback| for each item in |i
| |
312 Args: | |
313 items: extattributes or arguments | |
314 """ | |
315 count = 0 | |
316 for item in items: | |
317 callback(item) | |
318 count += 1 | |
319 if count < len(items): | |
320 out.write(', ') | |
321 | |
322 | |
323 def print_extattributes_of_member(extattributes, out): | |
324 """Print extattributes in a "member" object. | |
325 Args: | |
326 A list included as a value of "ExtAttributes" of "member" object | |
327 """ | |
328 def callback(extattribute): | |
329 out.write(extattribute['Name']) | |
330 | |
331 out.write('[') | |
332 print_items(extattributes, callback, out) | |
333 out.write(']') | |
334 | |
335 | |
336 def print_attributes(attributes, out): | |
337 """Print attributes in an "interface" object. | |
338 Args: | |
339 A list included as a value of "member" object named "Attributes" | |
340 """ | |
341 for attribute in attributes: | |
342 out.write(' ') | |
343 change_color_based_on_tag(attribute, out) | |
344 if attribute['ExtAttributes']: | |
345 print_extattributes_of_member(attribute['ExtAttributes'], out) | |
346 out.write(attribute['Type']) | |
347 out.write(' ') | |
348 out.writeln(attribute['Name']) | |
349 | |
350 | |
351 def print_arguments(arguments, out): | |
352 """Print arguments in a "members" object named "Operations". | |
353 Args: A list included as a value of "Arguments" of "member" object named | |
354 "Operations" | |
355 """ | |
356 def callback(argument): | |
357 out.write(argument['Name']) | |
358 | |
359 out.write('(') | |
360 print_items(arguments, callback, out) | |
361 out.writeln(')') | |
362 | |
363 | |
364 def print_operations(operations, out): | |
365 """Print operations in a "member" object. | |
366 Args: | |
367 A list included as a value of "member" object named "Operations" | |
368 """ | |
369 for operation in operations: | |
370 out.write(' ') | |
371 change_color_based_on_tag(operation, out) | |
372 if operation['ExtAttributes']: | |
373 print_extattributes_of_member(operation['ExtAttributes'], out) | |
374 out.write(operation['Type']) | |
375 out.write(' ') | |
376 if operation['Arguments']: | |
377 out.write(operation['Name']) | |
378 print_arguments(operation['Arguments'], out) | |
379 else: | |
380 out.writeln(operation['Name']) | |
381 | |
382 | |
383 def print_diff(diff, out): | |
384 """Print the diff on a shell. | |
385 Args: | |
386 A sorted diff | |
387 """ | |
388 for interface_name, interface in diff.iteritems(): | |
389 change_color_based_on_tag(interface, out) | |
390 out.change_color(Colorize.YELLOW) | |
391 out.write('[[') | |
392 out.write(interface_name) | |
393 out.writeln(']]') | |
394 out.reset_color() | |
395 for member_name, member in interface.iteritems(): | |
396 if member_name == 'ExtAttributes': | |
397 out.writeln('ExtAttributes') | |
398 print_extattributes(member, out) | |
399 elif member_name == 'Consts': | |
400 out.writeln(' Consts') | |
401 print_consts(member, out) | |
402 elif member_name == 'Attributes': | |
403 out.writeln(' Attributes') | |
404 print_attributes(member, out) | |
405 elif member_name == 'Operations': | |
406 out.writeln(' Operations') | |
407 print_operations(member, out) | |
408 out.reset_color() | |
409 | |
410 | |
411 def usage(): | |
412 """Show usage.""" | |
413 sys.stdout.write('Usage: print_diff.py <diff_file.json> <TAG>/<ALPHABET>\n') | |
Yuki
2015/09/25 07:12:37
<"TAG" | "ALPHABET">
shimadaa
2015/09/25 07:40:07
Done.
| |
414 | |
415 | |
416 def main(argv): | |
417 if len(argv) != 2: | |
418 usage() | |
419 exit(1) | |
420 json_data = argv[0] | |
421 order = argv[1] | |
422 diff = load_json_file(json_data) | |
423 if order == 'TAG': | |
424 sort_func = sort_diff_by_tags | |
425 elif order == 'ALPHABET': | |
426 sort_func = sort_diff_in_alphabetical_order | |
427 else: | |
428 usage() | |
429 exit(1) | |
430 sorted_diff = sort_func(diff) | |
431 out = Colorize(sys.stdout) | |
432 print_diff(sorted_diff, out) | |
433 | |
434 | |
435 if __name__ == '__main__': | |
436 main(sys.argv[1:]) | |
OLD | NEW |