| """Common config and logic for binary search tool |
| |
| This module serves two main purposes: |
| 1. Programatically include the utils module in PYTHONPATH |
| 2. Create the argument parsing shared between binary_search_state.py and |
| bisect.py |
| |
| The argument parsing is handled by populating _ArgsDict with all arguments. |
| _ArgsDict is required so that binary_search_state.py and bisect.py can share |
| the argument parsing, but treat them slightly differently. For example, |
| bisect.py requires that all argument defaults are suppressed so that overriding |
| can occur properly (i.e. only options that are explicitly entered by the user |
| end up in the resultant options dictionary). |
| |
| ArgumentDict inherits OrderedDict in order to preserve the order the args are |
| created so the help text is made properly. |
| """ |
| |
| from __future__ import print_function |
| |
| import argparse |
| import collections |
| import os |
| import sys |
| |
| # Programatically adding utils python path to PYTHONPATH |
| if os.path.isabs(sys.argv[0]): |
| utils_pythonpath = os.path.abspath('{0}/..'.format( |
| os.path.dirname(sys.argv[0]))) |
| else: |
| wdir = os.getcwd() |
| utils_pythonpath = os.path.abspath('{0}/{1}/..'.format( |
| wdir, os.path.dirname(sys.argv[0]))) |
| sys.path.append(utils_pythonpath) |
| |
| |
| class ArgumentDict(collections.OrderedDict): |
| """Wrapper around OrderedDict, represents CLI arguments for program. |
| |
| AddArgument enforces the following layout: |
| { |
| ['-n', '--iterations'] : { |
| 'dest': 'iterations', |
| 'type': int, |
| 'help': 'Number of iterations to try in the search.', |
| 'default': 50 |
| } |
| [arg_name1, arg_name2, ...] : { |
| arg_option1 : arg_option_val1, |
| ... |
| }, |
| ... |
| } |
| """ |
| _POSSIBLE_OPTIONS = ['action', 'nargs', 'const', 'default', 'type', 'choices', |
| 'required', 'help', 'metavar', 'dest'] |
| |
| def AddArgument(self, *args, **kwargs): |
| """Add argument to ArgsDict, has same signature as argparse.add_argument |
| |
| Emulates the the argparse.add_argument method so the internal OrderedDict |
| can be safely and easily populated. Each call to this method will have a 1-1 |
| corresponding call to argparse.add_argument once BuildArgParser is called. |
| |
| Args: |
| *args: The names for the argument (-V, --verbose, etc.) |
| **kwargs: The options for the argument, corresponds to the args of |
| argparse.add_argument |
| |
| Returns: |
| None |
| |
| Raises: |
| TypeError: if args is empty or if option in kwargs is not a valid |
| option for argparse.add_argument. |
| """ |
| if len(args) == 0: |
| raise TypeError('Argument needs at least one name') |
| |
| for key in kwargs: |
| if key not in self._POSSIBLE_OPTIONS: |
| raise TypeError('Invalid option "%s" for argument %s' % |
| (key, args[0])) |
| |
| self[args] = kwargs |
| |
| |
| _ArgsDict = ArgumentDict() |
| |
| |
| def GetArgsDict(): |
| """_ArgsDict singleton method""" |
| if not _ArgsDict: |
| _BuildArgsDict(_ArgsDict) |
| return _ArgsDict |
| |
| |
| def BuildArgParser(parser, override=False): |
| """Add all arguments from singleton ArgsDict to parser. |
| |
| Will take argparse parser and add all arguments in ArgsDict. Will ignore |
| the default and required options if override is set to True. |
| |
| Args: |
| parser: type argparse.ArgumentParser, will call add_argument for every item |
| in _ArgsDict |
| override: True if being called from bisect.py. Used to say that default and |
| required options are to be ignored |
| |
| Returns: |
| None |
| """ |
| ArgsDict = GetArgsDict() |
| |
| # Have no defaults when overriding |
| for arg_names, arg_options in ArgsDict.iteritems(): |
| if override: |
| arg_options = arg_options.copy() |
| arg_options.pop('default', None) |
| arg_options.pop('required', None) |
| |
| parser.add_argument(*arg_names, **arg_options) |
| |
| |
| def _BuildArgsDict(args): |
| """Populate ArgumentDict with all arguments""" |
| args.AddArgument('-n', |
| '--iterations', |
| dest='iterations', |
| type=int, |
| help='Number of iterations to try in the search.', |
| default=50) |
| args.AddArgument('-i', |
| '--get_initial_items', |
| dest='get_initial_items', |
| help=('Script to run to get the initial objects. ' |
| 'If your script requires user input ' |
| 'the --verbose option must be used')) |
| args.AddArgument('-g', |
| '--switch_to_good', |
| dest='switch_to_good', |
| help=('Script to run to switch to good. ' |
| 'If your switch script requires user input ' |
| 'the --verbose option must be used')) |
| args.AddArgument('-b', |
| '--switch_to_bad', |
| dest='switch_to_bad', |
| help=('Script to run to switch to bad. ' |
| 'If your switch script requires user input ' |
| 'the --verbose option must be used')) |
| args.AddArgument('-I', |
| '--install_script', |
| dest='install_script', |
| help=('Optional script to perform building, flashing, ' |
| 'and other setup before the test script runs.')) |
| args.AddArgument('-t', |
| '--test_script', |
| dest='test_script', |
| help=('Script to run to test the ' |
| 'output after packages are built.')) |
| args.AddArgument('-p', |
| '--prune', |
| dest='prune', |
| action='store_true', |
| default=False, |
| help='Continue until all bad items are found.') |
| # --prune False override, opposite of --prune |
| args.AddArgument('--noprune', |
| dest='prune', |
| action='store_false', |
| default=argparse.SUPPRESS) |
| args.AddArgument('-c', |
| '--noincremental', |
| dest='noincremental', |
| action='store_true', |
| default=False, |
| help='Do not propagate good/bad changes incrementally.') |
| # --noincremental False override, opposite of --noincremental |
| args.AddArgument('--incremental', |
| dest='noincremental', |
| action='store_false', |
| default=argparse.SUPPRESS) |
| args.AddArgument('-f', |
| '--file_args', |
| dest='file_args', |
| action='store_true', |
| default=False, |
| help='Use a file to pass arguments to scripts.') |
| # --file_args False override, opposite of --file_args |
| args.AddArgument('--nofile_args', |
| dest='file_args', |
| action='store_false', |
| default=argparse.SUPPRESS) |
| args.AddArgument('-v', |
| '--verify_level', |
| dest='verify_level', |
| type=int, |
| default=1, |
| help=('Check binary search assumptions N times ' |
| 'before starting.')) |
| args.AddArgument('-N', |
| '--prune_iterations', |
| dest='prune_iterations', |
| type=int, |
| help='Number of prune iterations to try in the search.', |
| default=100) |
| args.AddArgument('-V', |
| '--verbose', |
| dest='verbose', |
| action='store_true', |
| help='Print full output to console.') |
| # --verbose False override, opposite of --verbose |
| args.AddArgument('--noverbose', |
| dest='verbose', |
| action='store_false', |
| default=argparse.SUPPRESS) |
| args.AddArgument('-r', |
| '--resume', |
| dest='resume', |
| action='store_true', |
| help=('Resume bisection tool execution from state file.' |
| 'Useful if the last bisection was terminated ' |
| 'before it could properly finish.')) |