diff options
Diffstat (limited to 'binary_search_tool/common.py')
-rw-r--r-- | binary_search_tool/common.py | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/binary_search_tool/common.py b/binary_search_tool/common.py new file mode 100644 index 00000000..945270be --- /dev/null +++ b/binary_search_tool/common.py @@ -0,0 +1,261 @@ +"""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 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 StrToBool(str_in): + if str_in.lower() in ['true', 't', '1']: + return True + if str_in.lower() in ['false', 'f', '0']: + return False + + raise AttributeError('%s is not a valid boolean string' % str_in) + + +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', + '--test_setup_script', + dest='test_setup_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.')) + # No input (evals to False), + # --prune (evals to True), + # --prune=False, + # --prune=True + args.AddArgument( + '-p', + '--prune', + dest='prune', + nargs='?', + const=True, + default=False, + type=StrToBool, + metavar='bool', + help=('If True, continue until all bad items are found. ' + 'Defaults to False.')) + # No input (evals to False), + # --noincremental (evals to True), + # --noincremental=False, + # --noincremental=True + args.AddArgument( + '-c', + '--noincremental', + dest='noincremental', + nargs='?', + const=True, + default=False, + type=StrToBool, + metavar='bool', + help=('If True, don\'t propagate good/bad changes ' + 'incrementally. Defaults to False.')) + # No input (evals to False), + # --file_args (evals to True), + # --file_args=False, + # --file_args=True + args.AddArgument( + '-f', + '--file_args', + dest='file_args', + nargs='?', + const=True, + default=False, + type=StrToBool, + metavar='bool', + help=('Whether to use a file to pass arguments to scripts. ' + 'Defaults to False.')) + # No input (evals to True), + # --verify (evals to True), + # --verify=False, + # --verify=True + args.AddArgument( + '--verify', + dest='verify', + nargs='?', + const=True, + default=True, + type=StrToBool, + metavar='bool', + help=('Whether to run verify iterations before searching. ' + 'Defaults to True.')) + args.AddArgument( + '-N', + '--prune_iterations', + dest='prune_iterations', + type=int, + help='Number of prune iterations to try in the search.', + default=100) + # No input (evals to False), + # --verbose (evals to True), + # --verbose=False, + # --verbose=True + args.AddArgument( + '-V', + '--verbose', + dest='verbose', + nargs='?', + const=True, + default=False, + type=StrToBool, + metavar='bool', + help='If True, print full output to console.') + 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.')) |