| #!/usr/bin/env python |
| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Print a diff generated by generate_idl_diff.py. |
| Before printing, sort the diff in the alphabetical order or the order of |
| diffing tags. |
| Usage: print_idl_diff.py diff_file.json order |
| diff.json: |
| Output of generate_idl_diff.py. The json file contains a dictionary |
| that represents a diff between two different Chromium versions. The |
| structure of the dictionary is like below. |
| order: |
| Specify how to sort. Either by "ALPHABET" or "TAG". |
| """ |
| |
| from collections import OrderedDict |
| import sys |
| |
| from webkitpy.bindings.generate_idl_diff import load_json_file |
| from webkitpy.bindings.generate_idl_diff import EXTATTRIBUTES_AND_MEMBER_TYPES |
| from webkitpy.bindings.generate_idl_diff import DIFF_TAG |
| from webkitpy.bindings.generate_idl_diff import DIFF_TAG_ADDED |
| from webkitpy.bindings.generate_idl_diff import DIFF_TAG_DELETED |
| |
| |
| # pylint: disable=W0105 |
| """Refer to the explanation of generate_idl_diff.py's input files. |
| The deffference between the input structure of generate_idl_diff.py and |
| that of print_diff.py is whether diffing tags are included or not. |
| {'Interface': { |
| 'diff_tag': 'deleted' |
| 'ExtAttributes': [{'Name': '...' |
| 'diff_tag': 'deleted'}, |
| ..., |
| ], |
| 'Consts': [{'Type': '...', |
| 'Name': '...', |
| 'Value': '...' |
| 'diff_tag': 'deleted'}, |
| ..., |
| ], |
| 'Attributes': [{'Type': '...', |
| 'Name': '...', |
| 'ExtAttributes':[{'Name': '...'}, |
| ..., |
| ] |
| 'diff_tag': 'deleted'}, |
| ..., |
| ], |
| 'Operations': [{'Type': '...', |
| 'Name': '...', |
| 'ExtAttributes':[{'Name': '...'}, |
| ..., |
| ], |
| 'Arguments': [{'Type': '...', |
| 'Name': '...'}, |
| ..., |
| ] |
| 'diff_tag': 'deleted'}, |
| ..., |
| ], |
| 'Name': '...' |
| }, |
| { |
| 'ExtAttributes': [{'Name': '...'}, |
| ..., |
| ], |
| 'Consts': [{'Type': '...', |
| 'Name': '...', |
| 'Value': '...' |
| 'diff_tag': 'added'}, |
| ..., |
| ], |
| 'Attributes': [{'Type': '...', |
| 'Name': '...', |
| 'ExtAttributes':[{'Name': '...'}, |
| ..., |
| ]}, |
| ..., |
| ], |
| 'Operations': [{'Type': '...', |
| 'Name': '...', |
| 'ExtAttributes':[{'Name': '...'}, |
| ..., |
| ], |
| 'Arguments': [{'Type': '...', |
| 'Name': '...'}, |
| ..., |
| ] |
| 'diff_tag': 'deleted'}, |
| ..., |
| ], |
| 'Name': '...' |
| }, |
| ..., |
| } |
| """ |
| |
| |
| class Colorize(object): |
| """This class outputs a colored text to sys.stdout. |
| TODO(bashi): This class doesn't work on Windows. Provide a way to suppress |
| escape sequences. |
| """ |
| |
| BLACK = 30 |
| RED = 31 |
| GREEN = 32 |
| YELLOW = 33 |
| COLORS = (BLACK, RED, GREEN, YELLOW) |
| |
| def __init__(self, out): |
| self.out = out |
| |
| def reset_color(self): |
| """Reset text's color to default.""" |
| self.out.write('\033[0m') |
| |
| def change_color(self, color): |
| """Change text's color by specifing arguments. |
| Args: |
| color: A new color to change. It should be one of |COLORS|. |
| """ |
| if color in self.COLORS: |
| self.out.write('\033[' + str(color) + 'm') |
| else: |
| raise Exception('Unsupported color.') |
| |
| def writeln(self, string): |
| """Print text with a line-break.""" |
| self.out.write(string + '\n') |
| |
| def write(self, string): |
| """Print text without a line-break.""" |
| self.out.write(string) |
| |
| |
| def sort_member_types(interface): |
| """Sort the members in the order of EXTATTRIBUTES_AND_MEMBER_TYPES. |
| Args: |
| interface: An "interface" object |
| Returns: |
| A sorted "interface" object |
| """ |
| sorted_interface = OrderedDict() |
| for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| sorted_interface[member_type] = interface.get(member_type) |
| sorted_interface[DIFF_TAG] = interface.get(DIFF_TAG) |
| return sorted_interface |
| |
| |
| def group_by_tag(interface_or_member_list): |
| """Group members of |interface_or_member_list| by tags. |
| Args: |
| interface_or_member_list: A list of interface names or a list of "members" |
| Returns: |
| A tuple of (removed, added, unchanged) where |
| removed: A list of removed members |
| added: A list of added members |
| unspecified: A list of other members |
| """ |
| removed = [] |
| added = [] |
| unspecified = [] |
| for interface_or_member in interface_or_member_list: |
| if DIFF_TAG in interface_or_member: |
| if interface_or_member[DIFF_TAG] == DIFF_TAG_DELETED: |
| removed.append(interface_or_member) |
| elif interface_or_member[DIFF_TAG] == DIFF_TAG_ADDED: |
| added.append(interface_or_member) |
| else: |
| unspecified.append(interface_or_member) |
| return (removed, added, unspecified) |
| |
| |
| def sort_interface_names_by_tags(interfaces): |
| """Sort interface names as follows. |
| [names of deleted "interface"s |
| -> names of added "interface"s |
| -> names of other "interface"s] |
| Args: |
| interfaces: "interface" objects. |
| Returns: |
| A list of sorted interface names |
| """ |
| interface_list = interfaces.values() |
| removed, added, unspecified = group_by_tag(interface_list) |
| # pylint: disable=W0110 |
| removed = map(lambda interface: interface['Name'], removed) |
| # pylint: disable=W0110 |
| added = map(lambda interface: interface['Name'], added) |
| # pylint: disable=W0110 |
| unspecified = map(lambda interface: interface['Name'], unspecified) |
| sorted_interface_names = removed + added + unspecified |
| return sorted_interface_names |
| |
| |
| def sort_members_by_tags(interface): |
| """Sort members of a given interface in the order of diffing tags. |
| Args: |
| An "interface" object |
| Returns: |
| A sorted "interface" object |
| """ |
| sorted_interface = OrderedDict() |
| if DIFF_TAG in interface: |
| return interface |
| for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| member_list = interface[member_type] |
| removed, added, unspecified = group_by_tag(member_list) |
| sorted_interface[member_type] = removed + added + unspecified |
| return sorted_interface |
| |
| |
| def sort_diff_by_tags(interfaces): |
| """Sort an "interfaces" object in the order of diffing tags. |
| Args: |
| An "interfaces" object loaded by load_json_data(). |
| Returns: |
| A sorted "interfaces" object |
| """ |
| sorted_interfaces = OrderedDict() |
| sorted_interface_names = sort_interface_names_by_tags(interfaces) |
| for interface_name in sorted_interface_names: |
| interface = sort_members_by_tags(interfaces[interface_name]) |
| sorted_interfaces[interface_name] = sort_member_types(interface) |
| return sorted_interfaces |
| |
| |
| def sort_members_in_alphabetical_order(interface): |
| """Sort a "members" object in the alphabetical order. |
| Args: |
| An "interface" object |
| Returns: |
| A sorted "interface" object |
| """ |
| sorted_interface = OrderedDict() |
| for member_type in EXTATTRIBUTES_AND_MEMBER_TYPES: |
| sorted_members = sorted(interface[member_type], |
| key=lambda member: member['Name']) |
| sorted_interface[member_type] = sorted_members |
| return sorted_interface |
| |
| |
| def sort_diff_in_alphabetical_order(interfaces): |
| """Sort an "interfaces" object in the alphabetical order. |
| Args: |
| An "interfaces" object. |
| Returns: |
| A sorted "interfaces" object |
| """ |
| sorted_interfaces = OrderedDict() |
| for interface_name in sorted(interfaces.keys()): |
| interface = interfaces[interface_name] |
| sorted_interface = sort_members_in_alphabetical_order(interface) |
| sorted_interface[DIFF_TAG] = interface.get(DIFF_TAG) |
| sorted_interfaces[interface_name] = sorted_interface |
| return sorted_interfaces |
| |
| |
| def print_member_with_color(member, out): |
| """Print the "member" with a colored text. '+' is added to an added |
| "member". '-' is added to a removed "member". |
| Args: |
| member: A "member" object |
| """ |
| if DIFF_TAG in member: |
| if member[DIFF_TAG] == DIFF_TAG_DELETED: |
| out.change_color(Colorize.RED) |
| out.write('- ') |
| elif member[DIFF_TAG] == DIFF_TAG_ADDED: |
| out.change_color(Colorize.GREEN) |
| out.write('+ ') |
| else: |
| out.change_color(Colorize.BLACK) |
| out.write(' ') |
| |
| |
| def print_extattributes(extattributes, out): |
| """Print extattributes in an "interface" object. |
| Args: |
| A list of "ExtAttributes" in the "interface" object |
| """ |
| for extattribute in extattributes: |
| out.write(' ') |
| print_member_with_color(extattribute, out) |
| out.writeln(extattribute['Name']) |
| |
| |
| def print_consts(consts, out): |
| """Print consts in an "interface" object. |
| Args: |
| A list of "Consts" of the "interface" object |
| """ |
| for const in consts: |
| out.write(' ') |
| print_member_with_color(const, out) |
| out.write(str(const['Type'])) |
| out.write(' ') |
| out.write(const['Name']) |
| out.write(' ') |
| out.writeln(const['Value']) |
| |
| |
| def print_items(items, callback, out): |
| """Calls |callback| for each item in |items|, printing commas between |
| |callback| calls. |
| Args: |
| items: extattributes or arguments |
| """ |
| count = 0 |
| for item in items: |
| callback(item) |
| count += 1 |
| if count < len(items): |
| out.write(', ') |
| |
| |
| def print_extattributes_in_member(extattributes, out): |
| """Print extattributes in a "member" object. |
| Args: |
| A list of "ExtAttributes" in the "member" object |
| """ |
| def callback(extattribute): |
| out.write(extattribute['Name']) |
| |
| out.write('[') |
| print_items(extattributes, callback, out) |
| out.write(']') |
| |
| |
| def print_attributes(attributes, out): |
| """Print attributes in an "interface" object. |
| Args: |
| A list of "Attributes" in the "interface" object |
| """ |
| for attribute in attributes: |
| out.write(' ') |
| print_member_with_color(attribute, out) |
| if attribute['ExtAttributes']: |
| print_extattributes_in_member(attribute['ExtAttributes'], out) |
| out.write(str(attribute['Type'])) |
| out.write(' ') |
| out.writeln(attribute['Name']) |
| |
| |
| def print_arguments(arguments, out): |
| """Print arguments in a "members" object named "Operations". |
| Args: A list of "Arguments" |
| """ |
| def callback(argument): |
| out.write(argument['Name']) |
| |
| out.write('(') |
| print_items(arguments, callback, out) |
| out.writeln(')') |
| |
| |
| def print_operations(operations, out): |
| """Print operations in a "member" object. |
| Args: |
| A list of "Operations" |
| """ |
| for operation in operations: |
| out.write(' ') |
| print_member_with_color(operation, out) |
| if operation['ExtAttributes']: |
| print_extattributes_in_member(operation['ExtAttributes'], out) |
| out.write(str(operation['Type'])) |
| out.write(' ') |
| if operation['Arguments']: |
| out.write(operation['Name']) |
| print_arguments(operation['Arguments'], out) |
| else: |
| out.writeln(operation['Name']) |
| |
| |
| def print_diff(diff, out): |
| """Print the diff on a shell. |
| Args: |
| A sorted diff |
| """ |
| for interface_name, interface in diff.iteritems(): |
| print_member_with_color(interface, out) |
| out.change_color(Colorize.YELLOW) |
| out.write('[[') |
| out.write(interface_name) |
| out.writeln(']]') |
| out.reset_color() |
| for member_name, member in interface.iteritems(): |
| if member_name == 'ExtAttributes': |
| out.writeln('ExtAttributes') |
| print_extattributes(member, out) |
| elif member_name == 'Consts': |
| out.writeln(' Consts') |
| print_consts(member, out) |
| elif member_name == 'Attributes': |
| out.writeln(' Attributes') |
| print_attributes(member, out) |
| elif member_name == 'Operations': |
| out.writeln(' Operations') |
| print_operations(member, out) |
| out.reset_color() |
| |
| |
| def print_usage(): |
| """Show usage.""" |
| sys.stdout.write('Usage: print_diff.py <diff_file.json> <"TAG"|"ALPHABET">\n') |
| |
| |
| def main(argv): |
| if len(argv) != 2: |
| print_usage() |
| exit(1) |
| json_data = argv[0] |
| order = argv[1] |
| diff = load_json_file(json_data) |
| if order == 'TAG': |
| sort_func = sort_diff_by_tags |
| elif order == 'ALPHABET': |
| sort_func = sort_diff_in_alphabetical_order |
| else: |
| print_usage() |
| exit(1) |
| sorted_diff = sort_func(diff) |
| out = Colorize(sys.stdout) |
| print_diff(sorted_diff, out) |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |