#!/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 json import sys from generate_idl_diff import load_json_file from generate_idl_diff import EXTATTRIBUTES_AND_MEMBER_TYPES from generate_idl_diff import DIFF_TAG from generate_idl_diff import DIFF_TAG_ADDED from generate_idl_diff import DIFF_TAG_DELETED """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) removed = map(lambda interface: interface['Name'], removed) added = map(lambda interface: interface['Name'], added) 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 <"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:])