261 lines
7.5 KiB
Python
261 lines
7.5 KiB
Python
"""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.'))
|