Chromium Code Reviews| Index: tools/metrics/histograms/update_histogram_enum.py |
| diff --git a/tools/metrics/histograms/update_histogram_enum.py b/tools/metrics/histograms/update_histogram_enum.py |
| index f0a29e0fe91c7aaf1c0500e82c572c0d836c4059..a36854309414d58ceaf829a82db697fac30cdcb4 100644 |
| --- a/tools/metrics/histograms/update_histogram_enum.py |
| +++ b/tools/metrics/histograms/update_histogram_enum.py |
| @@ -25,224 +25,255 @@ HISTOGRAMS_PATH = path_util.GetHistogramsFile() |
| class UserError(Exception): |
| - def __init__(self, message): |
| - Exception.__init__(self, message) |
| + def __init__(self, message): |
|
Ilya Sherman
2017/04/26 00:18:50
Hmm, why did you add extra indentation to all of t
lunalu1
2017/04/27 18:31:42
My bad, I didn't realize chromium and Blink had di
|
| + Exception.__init__(self, message) |
| - @property |
| - def message(self): |
| - return self.args[0] |
| + @property |
| + def message(self): |
| + return self.args[0] |
| + |
|
Ilya Sherman
2017/04/26 00:18:50
nit: Please leave two blank lines between top-leve
|
| +class DuplicatedValue(Exception): |
| + """Exception raised for duplicated enum values. |
| + |
| + Attributes: |
| + first_label: First enum label that shares the duplicated enum value. |
| + second_label: Second enum label that shares the duplicated enum value. |
| + """ |
| + def __init__(self, first_label, second_label): |
| + self.first_label = first_label |
| + self.second_label = second_label |
| def Log(message): |
| - logging.info(message) |
| + logging.info(message) |
| def ReadHistogramValues(filename, start_marker, end_marker, strip_k_prefix): |
| - """Returns a dictionary of enum values and a pair of labels that have the same |
| - enum values, read from a C++ file. |
| - |
| - Args: |
| - filename: The unix-style path (relative to src/) of the file to open. |
| - start_marker: A regex that signifies the start of the enum values. |
| - end_marker: A regex that signifies the end of the enum values. |
| - strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| - 'k' should be stripped. |
| + """Creates a dictionary of enum values, read from a C++ file. |
| + |
| + Args: |
| + filename: The unix-style path (relative to src/) of the file to open. |
| + start_marker: A regex that signifies the start of the enum values. |
| + end_marker: A regex that signifies the end of the enum values. |
| + strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| + 'k' should be stripped. |
| + |
| + Returns: |
| + A dict mapping enum labels to the corresponding enum values. |
| + |
| + Raises: |
| + DuplicatedValue: An error when two enum labels share the same value. |
| """ |
| - # Read the file as a list of lines |
| - with open(path_util.GetInputFile(filename)) as f: |
| - content = f.readlines() |
| - |
| - START_REGEX = re.compile(start_marker) |
| - ITEM_REGEX = re.compile(r'^(\w+)') |
| - ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d*)') |
| - WRAPPED_INIT = re.compile(r'(\d+)') |
| - END_REGEX = re.compile(end_marker) |
| - |
| - iterator = iter(content) |
| - # Find the start of the enum |
| - for line in iterator: |
| - if START_REGEX.match(line.strip()): |
| - break |
| - |
| - enum_value = 0 |
| - result = {} |
| - for line in iterator: |
| - line = line.strip() |
| - # Exit condition: we reached last enum value |
| - if END_REGEX.match(line): |
| - break |
| - # Inside enum: generate new xml entry |
| - m = ITEM_REGEX_WITH_INIT.match(line) |
| - if m: |
| - label = m.group(1) |
| - if m.group(2): |
| - enum_value = int(m.group(2)) |
| - else: |
| - # Enum name is so long that the value wrapped to the next line |
| - next_line = next(iterator).strip() |
| - enum_value = int(WRAPPED_INIT.match(next_line).group(1)) |
| - else: |
| - m = ITEM_REGEX.match(line) |
| - if m: |
| - label = m.group(1) |
| - else: |
| - continue |
| - # If two enum labels have the same value |
| - if enum_value in result: |
| - return result, (result[enum_value], label) |
| - if strip_k_prefix: |
| - assert label.startswith('k'), "Enum " + label + " should start with 'k'." |
| - label = label[1:] |
| - result[enum_value] = label |
| - enum_value += 1 |
| - return result, None |
| + # Read the file as a list of lines |
| + with open(path_util.GetInputFile(filename)) as f: |
| + content = f.readlines() |
| + |
| + START_REGEX = re.compile(start_marker) |
| + ITEM_REGEX = re.compile(r'^(\w+)') |
| + ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d*)') |
| + WRAPPED_INIT = re.compile(r'(\d+)') |
| + END_REGEX = re.compile(end_marker) |
| + |
| + iterator = iter(content) |
| + # Find the start of the enum |
| + for line in iterator: |
| + if START_REGEX.match(line.strip()): |
| + break |
| + |
| + enum_value = 0 |
| + result = {} |
| + for line in iterator: |
| + line = line.strip() |
| + # Exit condition: we reached last enum value |
| + if END_REGEX.match(line): |
| + break |
| + # Inside enum: generate new xml entry |
| + m = ITEM_REGEX_WITH_INIT.match(line) |
| + if m: |
| + label = m.group(1) |
| + if m.group(2): |
| + enum_value = int(m.group(2)) |
| + else: |
| + # Enum name is so long that the value wrapped to the next line |
| + next_line = next(iterator).strip() |
| + enum_value = int(WRAPPED_INIT.match(next_line).group(1)) |
| + else: |
| + m = ITEM_REGEX.match(line) |
| + if m: |
| + label = m.group(1) |
| + else: |
| + continue |
| + # If two enum labels have the same value |
| + if enum_value in result: |
| + raise DuplicatedValue(result[enum_value], label) |
| + if strip_k_prefix: |
| + assert label.startswith('k'), ("Enum " + label + |
| + " should start with 'k'.") |
| + label = label[1:] |
| + result[enum_value] = label |
| + enum_value += 1 |
| + return result |
| def CreateEnumItemNode(document, value, label): |
| - """Creates an int element to append to an enum.""" |
| - item_node = document.createElement('int') |
| - item_node.attributes['value'] = str(value) |
| - item_node.attributes['label'] = label |
| - return item_node |
| + """Creates an int element to append to an enum.""" |
| + item_node = document.createElement('int') |
| + item_node.attributes['value'] = str(value) |
| + item_node.attributes['label'] = label |
| + return item_node |
| def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values, |
| source_enum_path, document): |
| - """Updates the enum node named |histogram_enum_name| based on the definition |
| - stored in |source_enum_values|. Existing items for which |source_enum_values| |
| - doesn't contain any corresponding data will be preserved. |source_enum_path| |
| - will be used to insert a comment. |
| - """ |
| - # Get a dom of <enum name=|histogram_enum_name| ...> node in |document|. |
| - for enum_node in document.getElementsByTagName('enum'): |
| - if enum_node.attributes['name'].value == histogram_enum_name: |
| - break |
| - else: |
| - raise UserError('No {0} enum node found'.format(histogram_enum_name)) |
| - |
| - new_item_nodes = {} |
| - new_comments = [] |
| - |
| - # Add a "Generated from (...)" comment. |
| - new_comments.append( |
| - document.createComment(' Generated from {0} '.format(source_enum_path))) |
| - |
| - # Create item nodes for each of the enum values. |
| - for value, label in source_enum_values.iteritems(): |
| - new_item_nodes[value] = CreateEnumItemNode(document, value, label) |
| - |
| - # Scan existing nodes in |enum_node| for old values and preserve them. |
| - # - Preserve comments other than the 'Generated from' comment. NOTE: |
| - # this does not preserve the order of the comments in relation to the |
| - # old values. |
| - # - Drop anything else. |
| - SOURCE_COMMENT_REGEX = re.compile('^ Generated from ') |
| - for child in enum_node.childNodes: |
| - if child.nodeName == 'int': |
| - value = int(child.attributes['value'].value) |
| - if not source_enum_values.has_key(value): |
| - new_item_nodes[value] = child |
| - # Preserve existing non-generated comments. |
| - elif (child.nodeType == minidom.Node.COMMENT_NODE and |
| - SOURCE_COMMENT_REGEX.match(child.data) is None): |
| - new_comments.append(child) |
| - |
| - # Update |enum_node|. First, remove everything existing. |
| - while enum_node.hasChildNodes(): |
| - enum_node.removeChild(enum_node.lastChild) |
| - |
| - # Add comments at the top. |
| - for comment in new_comments: |
| - enum_node.appendChild(comment) |
| - |
| - # Add in the new enums. |
| - for value in sorted(new_item_nodes.iterkeys()): |
| - enum_node.appendChild(new_item_nodes[value]) |
| + """Updates the enum node named |histogram_enum_name| based on the definition |
| + stored in |source_enum_values|. Existing items for which |
| + |source_enum_values| doesn't contain any corresponding data will be |
| + preserved. |source_enum_path| will be used to insert a comment. |
| + """ |
| + # Get a dom of <enum name=|histogram_enum_name| ...> node in |document|. |
| + for enum_node in document.getElementsByTagName('enum'): |
| + if enum_node.attributes['name'].value == histogram_enum_name: |
| + break |
| + else: |
| + raise UserError('No {0} enum node found'.format(histogram_enum_name)) |
| + |
| + new_item_nodes = {} |
| + new_comments = [] |
| + |
| + # Add a "Generated from (...)" comment. |
| + new_comments.append( |
| + document.createComment(' Generated from {0} '.format(source_enum_path))) |
| + |
| + # Create item nodes for each of the enum values. |
| + for value, label in source_enum_values.iteritems(): |
| + new_item_nodes[value] = CreateEnumItemNode(document, value, label) |
| + |
| + # Scan existing nodes in |enum_node| for old values and preserve them. |
| + # - Preserve comments other than the 'Generated from' comment. NOTE: |
| + # this does not preserve the order of the comments in relation to the |
| + # old values. |
| + # - Drop anything else. |
| + SOURCE_COMMENT_REGEX = re.compile('^ Generated from ') |
| + for child in enum_node.childNodes: |
| + if child.nodeName == 'int': |
| + value = int(child.attributes['value'].value) |
| + if not source_enum_values.has_key(value): |
| + new_item_nodes[value] = child |
| + # Preserve existing non-generated comments. |
| + elif (child.nodeType == minidom.Node.COMMENT_NODE and |
| + SOURCE_COMMENT_REGEX.match(child.data) is None): |
| + new_comments.append(child) |
| + |
| + # Update |enum_node|. First, remove everything existing. |
| + while enum_node.hasChildNodes(): |
| + enum_node.removeChild(enum_node.lastChild) |
| + |
| + # Add comments at the top. |
| + for comment in new_comments: |
| + enum_node.appendChild(comment) |
| + |
| + # Add in the new enums. |
| + for value in sorted(new_item_nodes.iterkeys()): |
| + enum_node.appendChild(new_item_nodes[value]) |
| def _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, |
| source_enum_path): |
| - """Reads old histogram from |histogram_enum_name| from |HISTOGRAMS_PATH|, and |
| - calculates new histogram from |source_enum_values| from |source_enum_path|, |
| - and returns both in XML format. |
| - """ |
| - Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH)) |
| - with open(HISTOGRAMS_PATH, 'rb') as f: |
| - histograms_doc = minidom.parse(f) |
| - f.seek(0) |
| - xml = f.read() |
| + """Reads old histogram from |histogram_enum_name| from |HISTOGRAMS_PATH|, |
| + and calculates new histogram from |source_enum_values| from |
| + |source_enum_path|, and returns both in XML format. |
| + """ |
| + Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH)) |
| + with open(HISTOGRAMS_PATH, 'rb') as f: |
| + histograms_doc = minidom.parse(f) |
| + f.seek(0) |
| + xml = f.read() |
| - Log('Comparing histograms enum with new enum definition.') |
| - UpdateHistogramDefinitions(histogram_enum_name, source_enum_values, |
| - source_enum_path, histograms_doc) |
| + Log('Comparing histograms enum with new enum definition.') |
| + UpdateHistogramDefinitions(histogram_enum_name, source_enum_values, |
| + source_enum_path, histograms_doc) |
| - new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc) |
| - return (xml, new_xml) |
| + new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc) |
| + return (xml, new_xml) |
| def HistogramNeedsUpdate(histogram_enum_name, source_enum_path, start_marker, |
| end_marker, strip_k_prefix = False): |
| - """Reads a C++ enum from a .h file and does a dry run of updating |
| - histograms.xml to match. Returns true if the histograms.xml file would be |
| - changed. |
| - |
| - Args: |
| - histogram_enum_name: The name of the XML <enum> attribute to update. |
| - source_enum_path: A unix-style path, relative to src/, giving |
| - the C++ header file from which to read the enum. |
| - start_marker: A regular expression that matches the start of the C++ enum. |
| - end_marker: A regular expression that matches the end of the C++ enum. |
| - strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| - 'k' should be stripped. |
| - """ |
| - Log('Reading histogram enum definition from "{0}".'.format(source_enum_path)) |
| - source_enum_values, duplicated_values = ReadHistogramValues( |
| - source_enum_path, start_marker, end_marker, strip_k_prefix) |
| - if duplicated_values: |
| - return False, duplicated_values |
| - |
| - (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, |
| - source_enum_path) |
| - return xml != new_xml, None |
| + """Reads a C++ enum from a .h file and does a dry run of updating |
| + histograms.xml to match. |
| + |
| + Args: |
| + histogram_enum_name: The name of the XML <enum> attribute to update. |
| + source_enum_path: A unix-style path, relative to src/, giving |
| + the C++ header file from which to read the enum. |
| + start_marker: A regular expression that matches the start of the C++ |
| + enum. |
| + end_marker: A regular expression that matches the end of the C++ enum. |
| + strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| + 'k' should be stripped. |
| + |
| + Returns: |
| + A boolean indicating whether the histograms.xml file would be changed. |
| + |
| + Raises: |
| + DuplicatedValue: An error when two enum labels share the same value. |
| + """ |
| + Log('Reading histogram enum definition from "{0}".'.format( |
| + source_enum_path)) |
| + try: |
| + source_enum_values, duplicated_values = ReadHistogramValues( |
| + source_enum_path, start_marker, end_marker, strip_k_prefix) |
| + except DuplicatedValue: |
| + raise |
|
Ilya Sherman
2017/04/26 00:18:50
There's no need to use a try/except here. The nat
|
| + |
| + (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, |
| + source_enum_values, |
| + source_enum_path) |
| + return xml != new_xml |
| def UpdateHistogramFromDict(histogram_enum_name, source_enum_values, |
| source_enum_path): |
| - """Updates |histogram_enum_name| enum in histograms.xml file with values |
| - from the {value: 'key'} dictionary |source_enum_values|. A comment is added |
| - to histograms.xml citing that the values in |histogram_enum_name| were |
| - sourced from |source_enum_path|. |
| - """ |
| - (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, source_enum_values, |
| - source_enum_path) |
| - if not diff_util.PromptUserToAcceptDiff( |
| - xml, new_xml, 'Is the updated version acceptable?'): |
| - Log('Cancelled.') |
| - return |
| + """Updates |histogram_enum_name| enum in histograms.xml file with values |
| + from the {value: 'key'} dictionary |source_enum_values|. A comment is added |
| + to histograms.xml citing that the values in |histogram_enum_name| were |
| + sourced from |source_enum_path|. |
| + """ |
| + (xml, new_xml) = _GetOldAndUpdatedXml(histogram_enum_name, |
| + source_enum_values, |
| + source_enum_path) |
| + if not diff_util.PromptUserToAcceptDiff( |
| + xml, new_xml, 'Is the updated version acceptable?'): |
| + Log('Cancelled.') |
| + return |
| - with open(HISTOGRAMS_PATH, 'wb') as f: |
| - f.write(new_xml) |
| + with open(HISTOGRAMS_PATH, 'wb') as f: |
| + f.write(new_xml) |
| - Log('Done.') |
| + Log('Done.') |
| def UpdateHistogramEnum(histogram_enum_name, source_enum_path, |
| start_marker, end_marker, strip_k_prefix = False): |
| - """Reads a C++ enum from a .h file and updates histograms.xml to match. |
| - |
| - Args: |
| - histogram_enum_name: The name of the XML <enum> attribute to update. |
| - source_enum_path: A unix-style path, relative to src/, giving |
| - the C++ header file from which to read the enum. |
| - start_marker: A regular expression that matches the start of the C++ enum. |
| - end_marker: A regular expression that matches the end of the C++ enum. |
| - strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| - 'k' should be stripped. |
| - """ |
| - |
| - Log('Reading histogram enum definition from "{0}".'.format(source_enum_path)) |
| - source_enum_values, ignored = ReadHistogramValues(source_enum_path, |
| - start_marker, end_marker, strip_k_prefix) |
| - |
| - UpdateHistogramFromDict(histogram_enum_name, source_enum_values, |
| - source_enum_path) |
| + """Reads a C++ enum from a .h file and updates histograms.xml to match. |
| + |
| + Args: |
| + histogram_enum_name: The name of the XML <enum> attribute to update. |
| + source_enum_path: A unix-style path, relative to src/, giving |
| + the C++ header file from which to read the enum. |
| + start_marker: A regular expression that matches the start of the C++ |
| + enum. |
| + end_marker: A regular expression that matches the end of the C++ enum. |
| + strip_k_prefix: Set to True if enum values are declared as kFoo and the |
| + 'k' should be stripped. |
| + """ |
| + |
| + Log('Reading histogram enum definition from "{0}".'.format( |
| + source_enum_path)) |
| + source_enum_values, ignored = ReadHistogramValues(source_enum_path, |
| + start_marker, |
| + end_marker, |
| + strip_k_prefix) |
| + |
| + UpdateHistogramFromDict(Histogram_enum_name, source_enum_values, |
|
Ilya Sherman
2017/04/26 00:18:50
nit: s/Histogram/histogram (text editor keyboard s
|
| + source_enum_path) |