Esempio n. 1
0
    def verify(self, val):
        ''' Verify against choices, if available

        Args:
            val: The value to be verified

        Raises:
            FailedVerificationError: If the checks failed
        '''
        if self.type == list and not isinstance(val, list):
            message = log.error('\'{}\' is not a list'.format(val))
            raise FailedVerificationError(message)
        if hasattr(self, 'choices'):
            if self.type == list:
                if hasattr(self, 'length') and len(val) != self.length:
                    message = log.error('Invalid length of list \'{}\', \
                            should be {}'.format(val, self.length))
                    raise FailedVerificationError(message)
                for v in val:
                    if v not in self.choices:
                        message = log.error('Invalid selection \'{}\'. \
                                Possible choices are \'{}\''.format(
                                    val, self.choices))
                        raise FailedVerificationError(message)
            elif val not in self.choices:
                message = log.error('Invalid selection \'{}\'. \
                    Possible choices are \'{}\''.format(val, self.choices))
                raise FailedVerificationError(message)
Esempio n. 2
0
    def __init__(self, **kwargs):
        ''' Setup the valid options through keywords.

        The ValidOption objects are configured through the use of
        keywords. A required keyword is 'type', which specifies the
        option type and takes care of casting.
        Additional keywords may be:
        'choices': takes a list with valid options
        'help': specifies a help string,
        'subtype': gives the type of options in within a type=list
                   object
        'length': specifies the number of entries within a type=list
                  object
        'alias': specifies aliases for the option

        Example:
        An option 'age' that shall use integer values, have a default
        of 18 and be addressable under the alisa 'a' on the cmd line:
        {'age': ValidOption(type=int, default=18,
                            help='The persons age', alias=['--a'])}

        A option with a list of 2 specific strings:
        {'code_type': ValidOption(
                type=list,
                subtype=str,
                default=['python', 'messy'],
                length=2,
                choices=['python', 'c', 'c++', 'messy', 'clean'],
                help='Specifiy what best describes the code')}

        Args:
            kwargs: The keywords for the option configuration.

        Raises:
            RuntimeError: On missing or invalid options.
        '''
        # defaults
        self.default = None
        self.help = 'Not specified'
        self.alias = []
        # process kwargs
        for key in self._required_attrs:
            if key not in kwargs.keys():
                message = log.error('Missing required option {} to \
                                    ValidOptions'.format(key))
                raise KeyError(message)
        for key, arg in kwargs.items():
            if key not in self._valid_attrs:
                message = log.error('Specified unknown option {} to \
                                    ValidOptions'.format(key))
                raise KeyError(message)
            setattr(self, key, arg)
        # special cases
        if self.type == list and not hasattr(self, 'subtype'):
            message = log.error('ValidOptions with type list \
                                    require a subtype')
            raise RuntimeError(message)
        # verify the default type
        if self.default is not None:
            self.verify(self.default)
Esempio n. 3
0
    def cast(self, val, ltype=None):
        ''' Cast to type, to subtype if type is a list.

        Args:
            val: The value to be cast.
            ltype: Manually specified type to cast to.

        Returns:
            The value, cast to the proper type.

        Raises:
            FailedCastError: If the value could not be cast.
        '''
        if ltype is None:
            ltype = self.type
        # special cases
        if ltype == bool:
            return self._cast_bool(val)
        elif ltype == list:
            return self._cast_list(val)
        # regular case
        try:
            return ltype(val)
        except ValueError:
            message = log.error('Unable to cast \'{}\' to \
                                type \'{}\''.format(val, ltype))
            raise FailedCastError(message)
Esempio n. 4
0
def str2bool(v):
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False

    message = log.error('Boolean value expected.')
    raise argparse.ArgumentTypeError(message)
Esempio n. 5
0
 def _cast_bool(val):
     ''' Handle the bool separately, as too much can be true or false '''
     if (val is True or
             (isinstance(val, str) and val.lower() == 'true') or
             (isinstance(val, int) and val != 0)):
         return True
     elif (val is False or
           (isinstance(val, str) and val.lower() == 'false') or
           (isinstance(val, int) and val == 0)):
         return False
     message = log.error('Unable to cast \
                     \'{}\' to type \'bool\''.format(val))
     raise FailedCastError(message)
Esempio n. 6
0
    def process(self, argv=sys.argv):
        ''' Process the defaults, the config file and the cmd args
        into the final configuration.

        Args:
            argv (sliceable): a list of arguments to be parsed

        Return:
            dict: a hierarchy of dicts with the assembled configuration
        '''
        default_opts = self.opt_tree.get_defaults()

        flat_opt = self.opt_tree.get_flat_validoptions()
        parser_handler = ParserHandler(flat_opt)

        common = parser_handler.get_common_group()
        common.add_argument(
                '-i', '--input',
                help='Provide the configuration through a file')
        common.add_argument(
                '--print-defaults', action=PrintOptionsAction,
                options_callback=self.get_defaults,
                help='Print the defaults in JSON format')
        common.add_argument(
                '--print-current-config', action=PrintOptionsAction,
                options_callback=self.get_current_config,
                help='Print the current configuration in JSON format')
        common.add_argument(
                '--version', action='version',
                version='%(prog)s TBD')

        flat_args = parser_handler.parse_args(argv)
        full_args = self.opt_tree.inflate_flat_options(flat_args)
        full_args = self._handle_cmd_specification(full_args)

        input_opts = {}
        if 'input' in flat_args:
            input_opts = self.read_input(full_args['input'])
            full_args.pop('input')

        # check if input file and cmd line args are conflicting
        for current_key, current_val in input_opts.items():
            if current_val is None and current_key in full_args:
                msg = log.error('RED', 'The command line argument {} '
                                       'conflicts with the deactivation in '
                                       'the input file.'.format(current_key))
                raise ValueError(msg)

        self._recursive_update(default_opts, input_opts)
        self._recursive_update(default_opts, full_args)
        return default_opts
Esempio n. 7
0
 def _cast_list(self, val):
     ''' Handle the casting of lists recursively '''
     if isinstance(val, list):
         return list(map(lambda x: self.cast(x, self.subtype), val))
     elif isinstance(val, str):
         mod = val.strip()
         if len(mod) >= 2 and mod[0] == '[' and mod[-1] == ']':
             mod = mod[1:-1].split(',')
             return list(map(lambda x: self.cast(x.strip(), self.subtype),
                             mod))
     message = log.error('Unable to cast \'{}\' \
                 to \type \'list({})\''.format(
                 val, self.subtype))
     raise FailedCastError(message)
Esempio n. 8
0
    def _recursive_update(cls, old, new):
        ''' Recursively update the old configuration hierarchy w/ a new one.

        Args:
            old (dict): a hierarchy of dicts to be updated
            new (dict): a hierarchy of dicts with which the prev config is
                updated
        '''
        for k, v in new.items():
            if k not in old:
                msg = log.error('RED', 'Tried to set the unknown option: {}\n'
                                       'Valid choices are {}'.format(k, old))
                raise KeyError(msg)
            if isinstance(new[k], dict) and isinstance(old[k], dict):
                cls._recursive_update(old[k], new[k])
            else:
                old[k] = v
Esempio n. 9
0
    def _recurse_cast_verify(cls, validator, config, trace):
        ''' Recurse the tree and validate/cast all leaves.

        Args:
            validator (dict): tree of dicts to verify against
            config (dict): tree of dicts to be verified/cast
            trace (list): the current stack of options for meaningfull error

        Returns:
            dict: tree with cast leaves
        '''
        if isinstance(validator, ValidOption):
            converted = validator.cast(config)
            validator.verify(converted)
            return converted
        ret = {}
        for k, v in config.items():
            try:
                ret[k] = cls._recurse_cast_verify(validator[k], v, [k]+trace)
            except KeyError as error:
                message = log.error('Invalid key\'{}\' in option\'{}\''.format(
                    k, ':'.join(reversed(trace))))
                raise InvalidKeyError(message) from error
        return ret