#!/usr/bin/env python # Copyright 2013 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. """Pretty-prints the histograms.xml file, alphabetizing tags, wrapping text at 80 chars, enforcing standard attribute ordering, and standardizing indentation. This is quite a bit more complicated than just calling tree.toprettyxml(); we need additional customization, like special attribute ordering in tags and wrapping text nodes, so we implement our own full custom XML pretty-printer. """ from __future__ import with_statement import argparse import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) import etree_util import presubmit_util import histogram_configuration_model class Error(Exception): pass UNIT_REWRITES = { 'mcs': 'microseconds', 'microsecond': 'microseconds', 'us': 'microseconds', 'millisecond': 'ms', 'milliseconds': 'ms', 'kb': 'KB', 'kB': 'KB', 'kilobytes': 'KB', 'kbits/s': 'kbps', 'mb': 'MB', 'mB': 'MB', 'megabytes': 'MB', 'mbits/s': 'mbps', 'percent': '%', 'Percent': '%', 'percentage': '%', } def canonicalizeUnits(tree): """Canonicalize the spelling of certain units in histograms.""" if tree.tag == 'histogram': units = tree.get('units') if units and units in UNIT_REWRITES: tree.set('units', UNIT_REWRITES[units]) for child in tree: canonicalizeUnits(child) def fixObsoleteOrder(tree): """Put obsolete tags at the beginning of histogram tags.""" obsoletes = [] for child in tree: if child.tag == 'obsolete': obsoletes.append(child) else: fixObsoleteOrder(child) for obsolete in obsoletes: tree.remove(obsolete) # Only keep the first obsolete tag. if obsoletes: tree.insert(0, obsoletes[0]) def DropNodesByTagName(tree, tag, dropped_nodes=[]): """Drop all nodes with named tag from the XML tree.""" removes = [] for child in tree: if child.tag == tag: removes.append(child) dropped_nodes.append(child) else: DropNodesByTagName(child, tag) for child in removes: tree.remove(child) def FixMisplacedHistogramsAndHistogramSuffixes(tree): """Fixes misplaced histogram and histogram_suffixes nodes.""" histograms = [] histogram_suffixes = [] def ExtractMisplacedHistograms(tree): """Gets and drops misplaced histograms and histogram_suffixes. Args: tree: The node of the xml tree. histograms: A list of histogram nodes inside histogram_suffixes_list node. This is a return element. histogram_suffixes: A list of histogram_suffixes nodes inside histograms node. This is a return element. """ for child in tree: if child.tag == 'histograms': DropNodesByTagName(child, 'histogram_suffixes', histogram_suffixes) elif child.tag == 'histogram_suffixes_list': DropNodesByTagName(child, 'histogram', histograms) else: ExtractMisplacedHistograms(child) ExtractMisplacedHistograms(tree) def AddBackMisplacedHistograms(tree): """Adds back those misplaced histogram and histogram_suffixes nodes.""" for child in tree: if child.tag == 'histograms': child.extend(histograms) elif child.tag == 'histogram_suffixes_list': child.extend(histogram_suffixes) else: AddBackMisplacedHistograms(child) AddBackMisplacedHistograms(tree) def PrettyPrintHistograms(raw_xml): """Pretty-print the given histograms XML. Args: raw_xml: The contents of the histograms XML file, as a string. Returns: The pretty-printed version. """ top_level_content = etree_util.GetTopLevelContent(raw_xml) root = etree_util.ParseXMLString(raw_xml) return top_level_content + PrettyPrintHistogramsTree(root) def PrettyPrintHistogramsTree(tree): """Pretty-print the given ElementTree element. Args: tree: The ElementTree element. Returns: The pretty-printed version as an XML string. """ # Prevent accidentally adding enums to histograms.xml DropNodesByTagName(tree, 'enums') FixMisplacedHistogramsAndHistogramSuffixes(tree) canonicalizeUnits(tree) fixObsoleteOrder(tree) return histogram_configuration_model.PrettifyTree(tree) def PrettyPrintEnums(raw_xml): """Pretty print the given enums XML.""" root = etree_util.ParseXMLString(raw_xml) # Prevent accidentally adding histograms to enums.xml DropNodesByTagName(root, 'histograms') DropNodesByTagName(root, 'histogram_suffixes_list') top_level_content = etree_util.GetTopLevelContent(raw_xml) formatted_xml = histogram_configuration_model.PrettifyTree(root) return top_level_content + formatted_xml def main(): """Pretty-prints the histograms or enums xml file at given relative path. Args: filepath: The relative path to xml file. --non-interactive: (Optional) Does not print log info messages and does not prompt user to accept the diff. --presubmit: (Optional) Simply prints a message if the input is not formatted correctly instead of modifying the file. --diff: (Optional) Prints diff to stdout rather than modifying the file. Example usage: pretty_print.py histograms_xml/Fingerprint/histograms.xml pretty_print.py enums.xml """ parser = argparse.ArgumentParser() parser.add_argument('filepath', help="relative path to XML file") # The following optional flags are used by common/presubmit_util.py parser.add_argument('--non-interactive', action="store_true") parser.add_argument('--presubmit', action="store_true") parser.add_argument('--diff', action="store_true") args = parser.parse_args() status = 0 if 'enums.xml' in args.filepath: status = presubmit_util.DoPresubmit(sys.argv, 'enums.xml', 'enums.before.pretty-print.xml', PrettyPrintEnums) elif 'histograms' in args.filepath: # Specify the individual directory of histograms.xml or # obsolete_histograms.xml. status = presubmit_util.DoPresubmit( sys.argv, args.filepath, # The backup filename should be # 'path/to/histograms.before.pretty-print.xml'. '.before.pretty-print.'.join(args.filepath.rsplit('.', 1)), PrettyPrintHistograms) sys.exit(status) if __name__ == '__main__': main()