Chromium Code Reviews| Index: appengine/findit/crash/flag_manager.py |
| diff --git a/appengine/findit/crash/flag_manager.py b/appengine/findit/crash/flag_manager.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9a13eb482635d6e5a49efe1c7243e951f7a70d52 |
| --- /dev/null |
| +++ b/appengine/findit/crash/flag_manager.py |
| @@ -0,0 +1,159 @@ |
| +# Copyright 2016 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. |
| + |
| +"""Module handling flags while doing stream parsing. |
| + |
| +``ParsingFlag`` has name, a condition function which returns boolean predicate |
|
wrengr
2016/12/28 23:34:09
"has name" -> "has a name"
"returns boolean predi
Sharu Jiang
2016/12/29 18:20:22
Done.
|
| +and a boolean value. We call a flag with ``True`` value is ``on``, and a flag |
|
wrengr
2016/12/28 23:34:10
"call a..." -> "We say that a flag with the boolea
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| +with ``False`` value is ``off``. |
| +The condition function is used to check whether a flag should be turned ``on`` |
|
wrengr
2016/12/28 23:34:09
should use "on" rather than ``on`` (ditto "off" ra
Sharu Jiang
2016/12/29 18:20:22
Done.
|
| +while it is ``off``. |
| + |
| +``FlagManager`` is a managing class of all the flags while doing stream parsing. |
|
wrengr
2016/12/28 23:34:09
"managing class of" -> "class for managing"
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| +In order to get tracked in stream parsing, all flags must be registered in |
|
wrengr
2016/12/28 23:34:10
"to get tracked in" -> "to be tracked during"
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| +``FlagManager`` with a certain group (differentiate different namespaces). |
| + |
| +For example: |
| + |
| + |
| +stacktrace = "blabla\nSUMMARY:\nblabla" |
| +flag = ParsingFlag('after_summary_flag', lambda line: 'SUMMARY:' in line, |
| + value=False) |
| +flag_manager = FlagManager() |
| +flag_manager.Register('stacktrace_group', flag) |
| + |
| +for line in stacktrace.splitlines(): |
| + flag_manager.ConditionallyTurnOnFlags(line) |
| + |
| + |
| +The ``after_summary_flag`` will be turned on (set the value to True) when |
| +parsing the second line, because the initial value of the flag is ``False`` and |
| +the condition function returns ``True`` for the second line. |
| + |
| +The flag will stay ``on``, until ``flag.Off()`` is called to set the value to |
| +``False`` explictly. |
| +""" |
| + |
| +from collections import defaultdict |
| + |
| + |
| +class ParsingFlag(object): |
| + """Represents a flag in stream parsing. |
| + |
| + This object serves like a delegation to manipulate flag of obj, |
|
wrengr
2016/12/28 23:34:10
"obj" -> "object"
Sharu Jiang
2016/12/29 18:20:22
Done.
|
| + condition will be used to evaluate the flag value. |
| + """ |
| + def __init__(self, name, condition=None, value=False): |
| + """ |
| + Args: |
| + name (str): Name of the flag. |
| + condition (function): Funtion that takes str line as input and returns |
| + boolean value indicating whether the flag should be turned on or not. |
| + Note, the condition should only be checked when the flag is off. |
|
wrengr
2016/12/28 23:34:09
The first sentence is ambiguous about the meaning
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| + value (bool): Initial value of the flag. |
| + """ |
| + self._name = name |
| + self._condition = condition |
| + self._value = value |
| + |
| + @property |
| + def name(self): |
| + return self._name |
| + |
| + @property |
| + def value(self): |
| + return self._value |
| + |
| + def On(self): |
|
wrengr
2016/12/28 23:34:10
I'd call this ``TurnOn`` rather than just ``On``.
Sharu Jiang
2016/12/29 18:20:22
Ok... I just thought that flag is "on" and flag ma
|
| + self._value = True |
| + |
| + def Off(self): |
|
wrengr
2016/12/28 23:34:09
ditto
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| + self._value = False |
| + |
| + def __nonzero__(self): |
| + return self._value |
| + |
| + __bool__ = __nonzero__ |
| + |
| + def ConditionallyTurnOn(self, line): |
| + """When the flag is off, turns on it if conditions met.""" |
| + if not self._value and self._condition and self._condition(line): |
|
wrengr
2016/12/28 23:34:09
is there a reason to check ``bool(self._condition)
Sharu Jiang
2016/12/29 18:20:22
No, but I think the performance should be the same
wrengr
2016/12/29 19:56:25
Performance would be marginally better (since you
Sharu Jiang
2017/01/03 22:13:27
Will address this in another cl
|
| + self.On() |
| + |
| + |
| +class FlagManager(object): |
| + """A manager to track all the registered flags. |
| + |
| + FlagManager collects and manages flags based on group names. FlagManager takes |
| + care of manipulating flags during the stream parsing, including evaluating |
| + based on the conditions of flags, and resetting flags. |
| + |
| + Note, flag manager only keeps distinct flags(with distinct flag name), one |
|
wrengr
2016/12/28 23:34:10
", one" -> ", and one"
Sharu Jiang
2016/12/29 18:20:21
Done.
|
| + flag cannot be registered with different groups. |
|
wrengr
2016/12/28 23:34:09
"different" -> "multiple"
Sharu Jiang
2016/12/29 18:20:22
Done.
|
| + """ |
| + |
| + def __init__(self): |
| + self.flag_groups = defaultdict(list) |
| + self.flags = {} |
| + |
| + def ClearFlags(self): |
| + """Deletes all the flags.""" |
| + self.flag_groups = defaultdict(list) |
| + self.flags = {} |
| + |
| + def Register(self, group_name, flag): |
| + """Registers a flag with a group. |
| + |
| + Flags under the same group should have the same scope. |
| + For example, in stacktrace parsing, the scope of a flag like |
| + ``after_sumary_line`` is the whole stacktrace, so it should be under |
| + ``stacktrace_flags`` group, while the scope of |
| + ``callstack_top_frame_has_no_symbol`` is callstack, and it should be |
| + under ``callstack_flags`` group. |
| + """ |
| + self.flag_groups[group_name].append(flag) |
| + self.flags[flag.name] = flag |
| + |
| + def GetAllFlags(self): |
| + """Returns all registered flags.""" |
| + return self.flags.values() |
| + |
| + def GetGroupFlags(self, group_name): |
| + """Returns a certain group of flags.""" |
| + return self.flag_groups.get(group_name, []) |
| + |
| + def ResetAllFlags(self): |
| + """Turns off all registered flags.""" |
| + for flag in self.GetAllFlags(): |
| + flag.Off() |
| + |
| + def ResetGroupFlags(self, group_name): |
| + """Turns off a certain group of flags.""" |
| + for flag in self.GetGroupFlags(group_name): |
| + flag.Off() |
| + |
| + def ConditionallyTurnOnFlags(self, line): |
| + """Turns on flags which are off and the conditions are met by the line.""" |
| + for flag in self.GetAllFlags(): |
| + flag.ConditionallyTurnOn(line) |
| + |
| + def Get(self, flag_name): |
| + """Gets the instance with flag_name.""" |
| + return self.flags.get(flag_name) |
| + |
| + def TurnOn(self, flag_name): |
| + """Sets the instance with flag_name to True.""" |
| + flag = self.flags.get(flag_name) |
| + if flag is None: |
| + return |
| + |
| + flag.On() |
| + |
| + def TurnOff(self, flag_name): |
| + """Sets the instance with flag_name to False.""" |
| + flag = self.flags.get(flag_name) |
| + if flag is None: |
| + return |
| + |
| + flag.Off() |