summaryrefslogtreecommitdiffstats
path: root/tools/metrics
diff options
context:
space:
mode:
authorholte <holte@chromium.org>2016-03-15 13:40:19 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-15 20:41:24 +0000
commit35aa85bd42c46d31c7019ce28c0471cfe7cae70f (patch)
treed57d2b9bf286b0b9f2ea0d876ab5efe09aaa8a25 /tools/metrics
parent7e89d114675a5dce29cfa1dcba1127b54cd78925 (diff)
downloadchromium_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.py32
-rw-r--r--tools/metrics/common/models.py30
-rw-r--r--tools/metrics/common/pretty_print_xml.py121
-rw-r--r--tools/metrics/histograms/print_style.py76
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("&", "&amp;").replace("<", "&lt;")
- s = s.replace("\"", "&quot;").replace(">", "&gt;")
+ """Returns escaped string for the given string |s|."""
+ s = s.replace('&', '&amp;').replace('<', '&lt;')
+ s = s.replace('\"', '&quot;').replace('>', '&gt;')
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)