Index: tools/metrics/actions/extract_actions.py |
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py |
index c7f550f6981f20f64ba124321b25639e6387a2e0..1c2836cf64e531398ec1aa10b6a3dfb2fbb02c8d 100755 |
--- a/tools/metrics/actions/extract_actions.py |
+++ b/tools/metrics/actions/extract_actions.py |
@@ -14,7 +14,6 @@ there are many possible actions. |
See also: |
base/metrics/user_metrics.h |
- http://wiki.corp.google.com/twiki/bin/view/Main/ChromeUserExperienceMetrics |
After extracting all actions, the content will go through a pretty print |
function to make sure it's well formatted. If the file content needs to be |
@@ -42,6 +41,19 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common')) |
import diff_util |
import pretty_print_xml |
+USER_METRICS_ACTION_RE = re.compile(r""" |
+ [^a-zA-Z] # Preceded by a non-alphabetical character. |
+ UserMetricsAction # Name of the function. |
+ \( # Opening parenthesis. |
+ \s* # Any amount of whitespace, including new lines. |
+ (.+?) # A sequence of characters for the param. |
+ \) # Closing parenthesis. |
+ """, |
+ re.VERBOSE | re.DOTALL # Verbose syntax and makes . also match new lines. |
+) |
+COMPUTED_ACTION_RE = re.compile(r'RecordComputedAction') |
+QUOTED_STRING_RE = re.compile(r'\"(.+?)\"') |
+ |
# Files that are known to use content::RecordComputedAction(), which means |
# they require special handling code in this script. |
# To add a new file, add it to this list and add the appropriate logic to |
@@ -410,6 +422,49 @@ def AddExtensionActions(actions): |
# Actions sent by 'Ok Google' Hotwording. |
actions.add('Hotword.HotwordTrigger') |
+ |
+class InvalidStatementException(Exception): |
+ """Indicates an invalid statement was found.""" |
+ |
+ |
+class ActionNameFinder: |
+ """Helper class to find action names in source code file.""" |
+ |
+ def __init__(self, path, contents): |
+ self.__path = path |
+ self.__pos = 0 |
+ self.__contents = contents |
+ |
+ def FindNextAction(self): |
+ """Finds the next action name in the file. |
+ |
+ Returns: |
+ The name of the action found or None if there are no more actions. |
+ Raises: |
+ InvalidStatementException if the next action statement is invalid |
+ and could not be parsed. There may still be more actions in the file, |
+ so FindNextAction() can continue to be called to find following ones. |
+ """ |
+ match = USER_METRICS_ACTION_RE.search(self.__contents, pos=self.__pos) |
+ if not match: |
+ return None |
+ match_start = match.start() |
+ self.__pos = match.end() |
+ match = QUOTED_STRING_RE.match(match.group(1)) |
+ if not match: |
+ self._RaiseException(match_start, self.__pos) |
+ return match.group(1) |
+ |
+ def _RaiseException(self, match_start, match_end): |
+ """Raises an InvalidStatementException for the specified code range.""" |
+ line_number = self.__contents.count('\n', 0, match_start) + 1 |
+ # Add 1 to |match_start| since the RE checks the preceding character. |
+ statement = self.__contents[match_start + 1:match_end] |
+ raise InvalidStatementException( |
+ '%s uses UserMetricsAction incorrectly on line %d:\n%s' % |
+ (self.__path, line_number, statement)) |
+ |
+ |
def GrepForActions(path, actions): |
"""Grep a source file for calls to UserMetrics functions. |
@@ -419,26 +474,25 @@ def GrepForActions(path, actions): |
""" |
global number_of_files_total |
number_of_files_total = number_of_files_total + 1 |
- # we look for the UserMetricsAction structure constructor |
- # this should be on one line |
- action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\("([^"]*)') |
- malformed_action_re = re.compile(r'[^a-zA-Z]UserMetricsAction\([^"]') |
- computed_action_re = re.compile(r'RecordComputedAction') |
+ |
+ finder = ActionNameFinder(path, open(path).read()) |
+ while True: |
+ try: |
+ action_name = finder.FindNextAction() |
+ if not action_name: |
+ break |
+ actions.add(action_name) |
+ except InvalidStatementException, e: |
+ log.warning(str(e)) |
+ |
line_number = 0 |
for line in open(path): |
line_number = line_number + 1 |
- match = action_re.search(line) |
- if match: # Plain call to RecordAction |
- actions.add(match.group(1)) |
- elif malformed_action_re.search(line): |
- # Warn if this line is using RecordAction incorrectly. |
- print >>sys.stderr, ('WARNING: %s has malformed call to RecordAction' |
- ' at %d' % (path, line_number)) |
- elif computed_action_re.search(line): |
+ if COMPUTED_ACTION_RE.search(line): |
# Warn if this file shouldn't be calling RecordComputedAction. |
if os.path.basename(path) not in KNOWN_COMPUTED_USERS: |
- print >>sys.stderr, ('WARNING: %s has RecordComputedAction at %d' % |
- (path, line_number)) |
+ log.warning('%s has RecordComputedAction statement on line %d' % |
+ (path, line_number)) |
class WebUIActionsParser(HTMLParser): |
"""Parses an HTML file, looking for all tags with a 'metric' attribute. |