"""
Responsible for handling the command line tools.
"""
import argparse
import logging
import textwrap
import itertools
import sys
import re
from argparse import ArgumentError
from functools import partial
# allow loading of modules based on current working directory
sys.path.insert(0, '')
[docs]def format_helptext(txt):
return argparse._(txt+'\n\n')
[docs]def args_help(parser):
parser.add_argument('--help', action='help', default=argparse.SUPPRESS,
help=format_helptext('Show this help message and exit'))
[docs]def args_common(parser):
parser.add_argument('--log-level', type=str.lower, default='warning', dest='log_level',
choices=list(map(str.lower, logging._nameToLevel.keys())),
help=format_helptext('Set the logging output level'))
parser.add_argument('--load', nargs='+', required=False, metavar='MODULE_NAME',
help=format_helptext(
'Load external code that may contain additional '
'classes for normalization, etc.\n'
'E.g. if the classes are contained in a python file '
'named myclasses.py in the directory where your are '
'calling `benchmarkstt` from, you would pass '
'`--load myclasses`.\n'
'All classes that are recognized will be automatically '
'documented in the `--help` command and available for use.'
))
[docs]def args_from_factory(action, factory, parser):
for conf in factory:
name = conf.name
docs = conf.docs
arguments = dict()
arguments['help'] = docs
arguments['nargs'] = 0
if len(conf.required_args) or len(conf.optional_args):
arguments['nargs'] = '+' if len(conf.required_args) else '*'
optionals = list(map(lambda x: '[%s]' % x, conf.optional_args))
arguments['metavar'] = tuple(conf.required_args + optionals)
arguments['action'] = action_with_arguments(action,
conf.required_args,
conf.optional_args)
parser.add_argument('--%s' % (name,), **arguments)
class _ActionWithArguments:
"""
Placeholder class to recognize an argument is a NormalizerAction in argparse
"""
[docs]def action_with_arguments(action, required_args, optional_args):
"""
Custom argparse action to support a variable amount of arguments
:param action: name of the action
:param list required_args: required arguments
:param list optional_args: optional arguments
:rtype: ActionWithArguments
"""
minlen = len(required_args)
maxlen = minlen + len(optional_args)
class ActionWithArguments(argparse.Action, _ActionWithArguments):
def __call__(self, parser, args, values, option_string=None):
if len(values) < minlen or len(values) > maxlen:
if minlen == maxlen:
lentxt = str(minlen)
else:
lentxt = 'between %d and % d' % (minlen, maxlen)
raise ArgumentError(self, 'requires %s arguments (got %d)' % (lentxt, len(values)))
if not hasattr(args, action):
setattr(args, action, [])
getattr(args, action).append([self.dest] + values)
return ActionWithArguments
# TODO: further augment formatter to give cleaner output
[docs]def args_complete(parser): # pragma: no cover
try:
# support argument completion if package is installed
import argcomplete
argcomplete.autocomplete(parser)
except ImportError:
pass
[docs]def create_parser(*args, **kwargs):
defaults = {
'add_help': False,
'formatter_class': CustomHelpFormatter,
'allow_abbrev': False,
}
kwargs = {k: kwargs.get(k, v) for k, v in defaults.items()}
parser = argparse.ArgumentParser(*args, **kwargs)
parser._optionals.title = 'named arguments'
return parser
[docs]def determine_log_level():
log_level = 'WARNING'
if '--log-level' in sys.argv:
idx = sys.argv.index('--log-level') + 1
if idx < len(sys.argv):
if sys.argv[idx].upper() in logging._nameToLevel:
log_level = sys.argv[idx].upper()
logging.basicConfig(level=log_level)
logging.getLogger().setLevel(log_level)
[docs]def preload_externals():
if '--load' not in sys.argv:
return
from benchmarkstt.factory import CoreFactory
for i in range(sys.argv.index('--load')+1, len(sys.argv)):
if sys.argv[i].startswith('-'):
break
CoreFactory.add_supported_namespace(sys.argv[i])
[docs]def before_parseargs():
# Set log-level so that also factory logs, etc. get output
determine_log_level()
# Preload requested modules so that it is also included in CLI and API help
preload_externals()