aboutsummaryrefslogtreecommitdiff
path: root/binary_search_tool
diff options
context:
space:
mode:
authorCassidy Burden <cburden@google.com>2016-06-24 13:14:01 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-06-28 15:27:23 -0700
commit2e37b144ecd1476964e2dc72ac6b5b2a81a32018 (patch)
treeaae5582a2e55e47efe9b8886d6948cff39ca369b /binary_search_tool
parent314ea5683c0b4c7658f6c3f659cb5c53218a613c (diff)
downloadtoolchain-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-xbinary_search_tool/binary_search_state.py94
-rwxr-xr-xbinary_search_tool/bisect.py10
-rw-r--r--binary_search_tool/common.py224
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.'))