diff options
author | holte <holte@chromium.org> | 2016-03-15 13:40:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-15 20:41:24 +0000 |
commit | 35aa85bd42c46d31c7019ce28c0471cfe7cae70f (patch) | |
tree | d57d2b9bf286b0b9f2ea0d876ab5efe09aaa8a25 /tools/metrics | |
parent | 7e89d114675a5dce29cfa1dcba1127b54cd78925 (diff) | |
download | chromium_src-35aa85bd42c46d31c7019ce28c0471cfe7cae70f.zip chromium_src-35aa85bd42c46d31c7019ce28c0471cfe7cae70f.tar.gz chromium_src-35aa85bd42c46d31c7019ce28c0471cfe7cae70f.tar.bz2 |
Sync pretty_print.py scripts with internal versions.
BUG=
Review URL: https://codereview.chromium.org/1787943002
Cr-Commit-Position: refs/heads/master@{#381302}
Diffstat (limited to 'tools/metrics')
-rw-r--r-- | tools/metrics/actions/print_style.py | 32 | ||||
-rw-r--r-- | tools/metrics/common/models.py | 30 | ||||
-rw-r--r-- | tools/metrics/common/pretty_print_xml.py | 121 | ||||
-rw-r--r-- | tools/metrics/histograms/print_style.py | 76 |
4 files changed, 193 insertions, 66 deletions
diff --git a/tools/metrics/actions/print_style.py b/tools/metrics/actions/print_style.py index a2d4ff7..39deb23 100644 --- a/tools/metrics/actions/print_style.py +++ b/tools/metrics/actions/print_style.py @@ -11,31 +11,55 @@ import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) import pretty_print_xml -# Desired order for tag and tag attributes. +# Desired order for tag and tag attributes. The *_ATTRIBUTE_ORDER maps are also +# used to determine the validity of tag names. # { tag_name: [attribute_name, ...] } ATTRIBUTE_ORDER = { 'action': ['name', 'not_user_triggered'], - 'owner': [], + 'action-suffix': ['separator', 'ordering'], + 'actions': [], + 'actions-suffixes': [], + 'affected-action': ['name'], 'description': [], 'obsolete': [], + 'owner': [], + 'suffix': ['name', 'label'], + 'with-suffix': ['name'], } # Tag names for top-level nodes whose children we don't want to indent. -TAGS_THAT_DONT_INDENT = ['actions'] +TAGS_THAT_DONT_INDENT = [ + 'actions', + 'actions-suffixes', +] # Extra vertical spacing rules for special tag names. # {tag_name: (newlines_after_open, newlines_before_close, newlines_after_close)} TAGS_THAT_HAVE_EXTRA_NEWLINE = { 'actions': (2, 1, 1), 'action': (1, 1, 1), + 'actions-suffixes': (2, 1, 1), + 'action-suffix': (1, 1, 1), } # Tags that we allow to be squished into a single line for brevity. TAGS_THAT_ALLOW_SINGLE_LINE = ['owner', 'description', 'obsolete'] +LOWERCASE_NAME_FN = lambda n: n.attributes['name'].value.lower() + +# Tags whose children we want to alphabetize. The key is the parent tag name, +# and the value is a pair of the tag name of the children we want to sort, +# and a key function that maps each child node to the desired sort key. +TAGS_ALPHABETIZATION_RULES = { + 'actions': ('action', LOWERCASE_NAME_FN), + 'action-suffix': ('affected-action', LOWERCASE_NAME_FN), +} + + def GetPrintStyle(): """Returns an XmlStyle object for pretty printing actions.""" return pretty_print_xml.XmlStyle(ATTRIBUTE_ORDER, TAGS_THAT_HAVE_EXTRA_NEWLINE, TAGS_THAT_DONT_INDENT, - TAGS_THAT_ALLOW_SINGLE_LINE) + TAGS_THAT_ALLOW_SINGLE_LINE, + TAGS_ALPHABETIZATION_RULES) diff --git a/tools/metrics/common/models.py b/tools/metrics/common/models.py index f675cdd..9e765b8 100644 --- a/tools/metrics/common/models.py +++ b/tools/metrics/common/models.py @@ -44,6 +44,7 @@ class NodeType(object): newlines that should be printed (after_open, before_close, after_close) single_line: True iff this node may be squashed into a single line. """ + def __init__(self, tag, dont_indent=False, extra_newlines=None, @@ -53,10 +54,10 @@ class NodeType(object): self.extra_newlines = extra_newlines self.single_line = single_line - def Unmarshall(self, node): + def Unmarshall(self, unused_node): return None - def Marshall(self, doc, obj): + def Marshall(self, unused_doc, unused_obj): return None def GetAttributes(self): @@ -74,6 +75,7 @@ class TextNodeType(NodeType): Args: tag: The name of XML tag for this type of node. """ + def __init__(self, tag, **kwargs): NodeType.__init__(self, tag, **kwargs) @@ -97,6 +99,7 @@ class ChildType(object): node_type: The NodeType of the child. multiple: True if the child can be repeated. """ + def __init__(self, attr, node_type, multiple): self.attr = attr self.node_type = node_type @@ -115,17 +118,18 @@ class ObjectNodeType(NodeType): string_attributes: A list of names of string attributes. children: A list of ChildTypes describing the objects children. """ + def __init__(self, tag, - int_attributes=[], - float_attributes=[], - string_attributes=[], - children=[], + int_attributes=None, + float_attributes=None, + string_attributes=None, + children=None, **kwargs): NodeType.__init__(self, tag, **kwargs) - self.int_attributes = int_attributes - self.float_attributes = float_attributes - self.string_attributes = string_attributes - self.children = children + self.int_attributes = int_attributes or [] + self.float_attributes = float_attributes or [] + self.string_attributes = string_attributes or [] + self.children = children or [] def __str__(self): return 'ObjectNodeType("%s")' % self.tag @@ -142,7 +146,7 @@ class ObjectNodeType(NodeType): obj[attr] = float(node.getAttribute(attr)) for attr in self.string_attributes: - obj[attr] = node.getAttribute(attr) + obj[attr] = unicode(node.getAttribute(attr)) for child in self.children: nodes = node.getElementsByTagName(child.node_type.tag) @@ -190,6 +194,7 @@ class DocumentType(object): Args: root_type: A NodeType describing the root tag of the document. """ + def __init__(self, root_type): self.root_type = root_type @@ -205,7 +210,8 @@ class DocumentType(object): {t: types[t].GetAttributes() for t in types}, {t: types[t].extra_newlines for t in types if types[t].extra_newlines}, [t for t in types if types[t].dont_indent], - [t for t in types if types[t].single_line]) + [t for t in types if types[t].single_line], + {}) def ToXML(self, comments, obj): doc = minidom.Document() diff --git a/tools/metrics/common/pretty_print_xml.py b/tools/metrics/common/pretty_print_xml.py index ad37aa1..595cabe 100644 --- a/tools/metrics/common/pretty_print_xml.py +++ b/tools/metrics/common/pretty_print_xml.py @@ -2,9 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Utility file for pretty print xml file. +"""Utility file for pretty printing xml file. -The function PrettyPrintNode will be used for formatting both histograms.xml +The function PrettyPrintXml will be used for formatting both histograms.xml and actions.xml. """ @@ -33,10 +33,9 @@ def LastLineLength(s): def XmlEscape(s): - """XML-escapes the given string, replacing magic characters (&<>") with their - escaped equivalents.""" - s = s.replace("&", "&").replace("<", "<") - s = s.replace("\"", """).replace(">", ">") + """Returns escaped string for the given string |s|.""" + s = s.replace('&', '&').replace('<', '<') + s = s.replace('\"', '"').replace('>', '>') return s @@ -44,12 +43,80 @@ class XmlStyle(object): """A class that stores all style specification for an output xml file.""" def __init__(self, attribute_order, tags_that_have_extra_newline, - tags_that_dont_indent, tags_that_allow_single_line): - # List of tag names for top-level nodes whose children are not indented. + tags_that_dont_indent, tags_that_allow_single_line, + tags_alphabetization_rules): self.attribute_order = attribute_order self.tags_that_have_extra_newline = tags_that_have_extra_newline self.tags_that_dont_indent = tags_that_dont_indent self.tags_that_allow_single_line = tags_that_allow_single_line + self.tags_alphabetization_rules = tags_alphabetization_rules + + def PrettyPrintXml(self, tree): + tree = self._TransformByAlphabetizing(tree) + tree = self.PrettyPrintNode(tree) + return tree + + def _UnsafeAppendChild(self, parent, child): + """Append child to parent's list of children. + + It ignores the possibility that the child is already in another node's + childNodes list. Requires that the previous parent of child is discarded + (to avoid non-tree DOM graphs). This can provide a significant speedup as + O(n^2) operations are removed (in particular, each child insertion avoids + the need to traverse the old parent's entire list of children). + + Args: + parent: the parent node to be appended to. + child: the child node to append to |parent| node. + """ + child.parentNode = None + parent.appendChild(child) + child.parentNode = parent + + def _TransformByAlphabetizing(self, node): + """Transform the given XML by alphabetizing nodes. + + Args: + node: The minidom node to transform. + + Returns: + The minidom node, with children appropriately alphabetized. Note that the + transformation is done in-place, i.e. the original minidom tree is + modified directly. + """ + if node.nodeType != xml.dom.minidom.Node.ELEMENT_NODE: + for c in node.childNodes: + self._TransformByAlphabetizing(c) + return node + + # Element node with a tag name that we alphabetize the children of? + if node.tagName in self.tags_alphabetization_rules: + # Put subnodes in a list of node, key pairs to allow for custom sorting. + subtag, key_function = self.tags_alphabetization_rules[node.tagName] + subnodes = [] + last_key = -1 + for c in node.childNodes: + if (c.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and + c.tagName == subtag): + last_key = key_function(c) + # Subnodes that we don't want to rearrange use the last node's key, + # so they stay in the same relative position. + subnodes.append((c, last_key)) + + # Sort the subnode list. + subnodes.sort(key=lambda pair: pair[1]) + + # Re-add the subnodes, transforming each recursively. + while node.firstChild: + node.removeChild(node.firstChild) + for (c, _) in subnodes: + self._UnsafeAppendChild(node, self._TransformByAlphabetizing(c)) + return node + + # Recursively handle other element nodes and other node types. + for c in node.childNodes: + self._TransformByAlphabetizing(c) + return node def PrettyPrintNode(self, node, indent=0): """Pretty-prints the given XML node at the given indent level. @@ -62,7 +129,7 @@ class XmlStyle(object): The pretty-printed string (including embedded newlines). Raises: - Error if the XML has unknown tags or attributes. + Error: if the XML has unknown tags or attributes. """ # Handle the top-level document node. if node.nodeType == xml.dom.minidom.Node.DOCUMENT_NODE: @@ -84,18 +151,24 @@ class XmlStyle(object): # Split the text into paragraphs at blank line boundaries. paragraphs = [[]] for l in lines: - if len(l.strip()) == 0 and len(paragraphs[-1]) > 0: + if paragraphs[-1] and not l.strip(): paragraphs.append([]) else: paragraphs[-1].append(l) # Remove trailing empty paragraph if present. - if len(paragraphs) > 0 and len(paragraphs[-1]) == 0: + if paragraphs and not paragraphs[-1]: paragraphs = paragraphs[:-1] # Wrap each paragraph and separate with two newlines. return '\n\n'.join([wrapper.fill('\n'.join(p)) for p in paragraphs]) # Handle element nodes. if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: + # Check if tag name is valid. + if node.tagName not in self.attribute_order: + logging.error('Unrecognized tag "%s"', node.tagName) + raise Error('Unrecognized tag "%s"', node.tagName) + + # Newlines. newlines_after_open, newlines_before_close, newlines_after_close = ( self.tags_that_have_extra_newline.get(node.tagName, (1, 1, 0))) # Open the tag. @@ -110,20 +183,20 @@ class XmlStyle(object): attributes = node.attributes.keys() if attributes: # Reorder the attributes. - if node.tagName not in self.attribute_order: - unrecognized_attributes = attributes - else: - unrecognized_attributes = ( - [a for a in attributes - if a not in self.attribute_order[node.tagName]]) - attributes = [a for a in self.attribute_order[node.tagName] - if a in attributes] + unrecognized_attributes = ( + [a for a in attributes + if a not in self.attribute_order[node.tagName]]) + attributes = [a for a in self.attribute_order[node.tagName] + if a in attributes] for a in unrecognized_attributes: logging.error( - 'Unrecognized attribute "%s" in tag "%s"' % (a, node.tagName)) + 'Unrecognized attribute "%s" in tag "%s"', a, node.tagName) if unrecognized_attributes: - raise Error() + raise Error( + 'Unrecognized attributes {0} in tag "{1}"'.format( + ', '.join('"{0}"'.format(a) for a in unrecognized_attributes), + node.tagName)) for a in attributes: value = XmlEscape(node.attributes[a].value) @@ -164,7 +237,7 @@ class XmlStyle(object): # Recursively pretty-print the child nodes. child_nodes = [self.PrettyPrintNode(n, indent=new_indent) for n in child_nodes] - child_nodes = [c for c in child_nodes if len(c.strip()) > 0] + child_nodes = [c for c in child_nodes if c.strip()] # Determine whether we can fit the entire node on a single line. close_tag = '</%s>' % node.tagName @@ -189,5 +262,5 @@ class XmlStyle(object): # Ignore other node types. This could be a processing instruction # (<? ... ?>) or cdata section (<![CDATA[...]]!>), neither of which are # legal in the histograms XML at present. - logging.error('Ignoring unrecognized node data: %s' % node.toxml()) - raise Error() + logging.error('Ignoring unrecognized node data: %s', node.toxml()) + raise Error('Ignoring unrecognized node data: {0}'.format(node.toxml())) diff --git a/tools/metrics/histograms/print_style.py b/tools/metrics/histograms/print_style.py index f1d7979..4e0fdd2 100644 --- a/tools/metrics/histograms/print_style.py +++ b/tools/metrics/histograms/print_style.py @@ -7,48 +7,71 @@ import os import sys +# Import the metrics/common module for pretty print xml. sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) import pretty_print_xml -# Desired order for tag attributes; attributes listed here will appear first, -# and in the same order as in these lists. +# Desired order for tag and tag attributes. The *_ATTRIBUTE_ORDER maps are also +# used to determine the validity of tag names. # { tag_name: [attribute_name, ...] } ATTRIBUTE_ORDER = { - 'enum': ['name', 'type'], - 'histogram': ['name', 'enum', 'units'], - 'int': ['value', 'label'], - 'histogram_suffixes': ['name', 'separator', 'ordering'], - 'suffix': ['name', 'label'], - 'affected-histogram': ['name'], - 'with-suffix': ['name'], + 'affected-histogram': ['name'], + 'detail': [], + 'details': [], + 'enum': ['name', 'type'], + 'enums': [], + # TODO(yiyaoliu): Remove fieldtrial related pieces when it is not used. + 'fieldtrial': ['name', 'separator', 'ordering'], + 'histogram': ['name', 'enum', 'units'], + 'histogram-configuration': ['logsource'], + 'histogram_suffixes': ['name', 'separator', 'ordering'], + 'histogram_suffixes_list': [], + 'histograms': [], + 'int': ['value', 'label'], + 'group': ['name', 'label'], + 'obsolete': [], + 'owner': [], + 'suffix': ['name', 'label'], + 'summary': [], + 'with-group': ['name'], + 'with-suffix': ['name'], } # Tag names for top-level nodes whose children we don't want to indent. TAGS_THAT_DONT_INDENT = [ - 'histogram-configuration', - 'histograms', - 'histogram_suffixes_list', - 'enums' + 'histogram-configuration', + 'histograms', + 'histogram_suffixes_list', + 'enums', ] # Extra vertical spacing rules for special tag names. # {tag_name: (newlines_after_open, newlines_before_close, newlines_after_close)} TAGS_THAT_HAVE_EXTRA_NEWLINE = { - 'histogram-configuration': (2, 1, 1), - 'histograms': (2, 1, 1), - 'histogram': (1, 1, 1), - 'histogram_suffixes_list': (2, 1, 1), - 'histogram_suffixes': (1, 1, 1), - 'enums': (2, 1, 1), - 'enum': (1, 1, 1), + 'histogram-configuration': (2, 1, 1), + 'histograms': (2, 1, 1), + 'histogram_suffixes_list': (2, 1, 1), + 'histogram_suffixes': (1, 1, 1), + 'enums': (2, 1, 1), + 'histogram': (1, 1, 1), + 'enum': (1, 1, 1), } # Tags that we allow to be squished into a single line for brevity. -TAGS_THAT_ALLOW_SINGLE_LINE = [ - 'summary', - 'int', - 'owner', -] +TAGS_THAT_ALLOW_SINGLE_LINE = ['summary', 'int', 'owner'] + +LOWERCASE_NAME_FN = lambda n: n.attributes['name'].value.lower() + +# Tags whose children we want to alphabetize. The key is the parent tag name, +# and the value is a pair of the tag name of the children we want to sort, +# and a key function that maps each child node to the desired sort key. +TAGS_ALPHABETIZATION_RULES = { + 'histograms': ('histogram', LOWERCASE_NAME_FN), + 'enums': ('enum', LOWERCASE_NAME_FN), + 'enum': ('int', lambda n: n.attributes['value'].value.lower()), + 'histogram_suffixes_list': ('histogram_suffixes', LOWERCASE_NAME_FN), + 'histogram_suffixes': ('affected-histogram', LOWERCASE_NAME_FN), +} def GetPrintStyle(): @@ -56,4 +79,5 @@ def GetPrintStyle(): return pretty_print_xml.XmlStyle(ATTRIBUTE_ORDER, TAGS_THAT_HAVE_EXTRA_NEWLINE, TAGS_THAT_DONT_INDENT, - TAGS_THAT_ALLOW_SINGLE_LINE) + TAGS_THAT_ALLOW_SINGLE_LINE, + TAGS_ALPHABETIZATION_RULES) |