diff options
author | Cassidy Burden <cburden@google.com> | 2016-06-24 13:14:01 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-06-28 15:27:23 -0700 |
commit | 2e37b144ecd1476964e2dc72ac6b5b2a81a32018 (patch) | |
tree | aae5582a2e55e47efe9b8886d6948cff39ca369b /binary_search_tool | |
parent | 314ea5683c0b4c7658f6c3f659cb5c53218a613c (diff) | |
download | toolchain-utils-2e37b144ecd1476964e2dc72ac6b5b2a81a32018.tar.gz |
Add common module for binary search tool
common module holds common logic between bisect and
binary_search_state for adding utils to PYTHONPATH
and for building the argument parser.
TEST=Run unit tests, run cros_pkg system test
Change-Id: I4085b28de011132c53b8fddf06e8745042ef8ea2
Reviewed-on: https://chrome-internal-review.googlesource.com/266996
Commit-Ready: Cassidy Burden <cburden@google.com>
Tested-by: Cassidy Burden <cburden@google.com>
Reviewed-by: Luis Lozano <llozano@chromium.org>
Diffstat (limited to 'binary_search_tool')
-rwxr-xr-x | binary_search_tool/binary_search_state.py | 94 | ||||
-rwxr-xr-x | binary_search_tool/bisect.py | 10 | ||||
-rw-r--r-- | binary_search_tool/common.py | 224 |
3 files changed, 230 insertions, 98 deletions
diff --git a/binary_search_tool/binary_search_state.py b/binary_search_tool/binary_search_state.py index 997190d2..c584b5ef 100755 --- a/binary_search_tool/binary_search_state.py +++ b/binary_search_tool/binary_search_state.py @@ -10,15 +10,9 @@ import pickle import sys import tempfile -# Programtically 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) +# Adds utils to PYTHONPATH +import common + # Now we do import from utils from utils import command_executer from utils import logger @@ -343,6 +337,7 @@ def _CanonicalizeScript(script_name): if not script_name.startswith('/'): return os.path.join('.', script_name) + def Run(get_initial_items, switch_to_good, switch_to_bad, test_script, install_script=None, iterations=50, prune=True, noincremental=False, file_args=False, verify_level=1, prune_iterations=100, verbose=False, @@ -387,86 +382,7 @@ def Main(argv): # Common initializations parser = argparse.ArgumentParser() - parser.add_argument('-n', - '--iterations', - dest='iterations', - type=int, - help='Number of iterations to try in the search.', - default=50) - parser.add_argument('-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')) - parser.add_argument('-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')) - parser.add_argument('-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')) - parser.add_argument('-I', - '--install_script', - dest='install_script', - default=None, - help=('Optional script to perform building, flashing, ' - 'and other setup before the test script runs.')) - parser.add_argument('-t', - '--test_script', - dest='test_script', - help=('Script to run to test the ' - 'output after packages are built.')) - parser.add_argument('-p', - '--prune', - dest='prune', - action='store_true', - default=False, - help=('Script to run to test the output after ' - 'packages are built.')) - parser.add_argument('-c', - '--noincremental', - dest='noincremental', - action='store_true', - default=False, - help='Do not propagate good/bad changes incrementally.') - parser.add_argument('-f', - '--file_args', - dest='file_args', - action='store_true', - default=False, - help='Use a file to pass arguments to scripts.') - parser.add_argument('-v', - '--verify_level', - dest='verify_level', - type=int, - default=1, - help=('Check binary search assumptions N times ' - 'before starting.')) - parser.add_argument('-N', - '--prune_iterations', - dest='prune_iterations', - type=int, - help='Number of prune iterations to try in the search.', - default=100) - parser.add_argument('-V', - '--verbose', - dest='verbose', - action='store_true', - help='Print full output to console.') - parser.add_argument('-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.')) - + common.BuildArgParser(parser) logger.GetLogger().LogOutput(' '.join(argv)) options = parser.parse_args(argv) diff --git a/binary_search_tool/bisect.py b/binary_search_tool/bisect.py index 007531c6..be6db75e 100755 --- a/binary_search_tool/bisect.py +++ b/binary_search_tool/bisect.py @@ -5,17 +5,9 @@ from __future__ import print_function import abc import argparse -import os import sys +import common -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) from utils import command_executer from utils import logger diff --git a/binary_search_tool/common.py b/binary_search_tool/common.py new file mode 100644 index 00000000..c1e938fa --- /dev/null +++ b/binary_search_tool/common.py @@ -0,0 +1,224 @@ +"""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=('Script to run to test the output after ' + 'packages are built.')) + # --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.')) |