def _add_argument(self, *name_or_flags, **kwargs) -> None: """Adds an argument to self (i.e. the super class ArgumentParser). Sets the following attributes of kwargs when not explicitly provided: - type: Set to the type annotation of the argument. - default: Set to the default value of the argument (if provided). - required: True if a default value of the argument is not provided, False otherwise. - action: Set to "store_true" if the argument is a required bool or a bool with default value False. Set to "store_false" if the argument is a bool with default value True. - nargs: Set to "*" if the type annotation is List[str], List[int], or List[float]. - help: Set to the argument documentation from the class docstring. :param name_or_flags: Either a name or a list of option strings, e.g. foo or -f, --foo. :param kwargs: Keyword arguments. """ # Get variable name variable = get_dest(*name_or_flags, **kwargs) # Get default if not specified if hasattr(self, variable): kwargs['default'] = kwargs.get('default', getattr(self, variable)) # Set required if option arg if is_option_arg(*name_or_flags) and variable != 'help': kwargs['required'] = kwargs.get('required', not hasattr(self, variable)) if len(name_or_flags) == 1 and name_or_flags[0][:2] == "--": # expand attributes such that ("--attribute",) becomes ("--attribute", "-a") cvar_names = {n: None for n in self.class_variables} for long_name in self.class_variables: preferred_shorts = [] for char in long_name: # collect eligible characters from long_name as preferred options if (char in ascii_letters and char not in preferred_shorts and not any( [char == v for v in cvar_names.values() if v])): preferred_shorts += char other_shorts = [ asc for asc in ascii_letters if asc not in long_name and asc not in preferred_shorts ] for char in preferred_shorts + other_shorts: if char == "h": # avoiding "h" because it overlaps with default behavior of ArgumentParser.add_help continue if not any( [char == v for v in set(cvar_names.values()) if v]): short_name = char break cvar_names[long_name] = short_name if cvar_names and name_or_flags[0][2:] in cvar_names: name_or_flags = (f"{name_or_flags[0]}", f"-{cvar_names[name_or_flags[0][2:]]}") # Set help if necessary if 'help' not in kwargs: kwargs['help'] = '(' # Type if variable in self._annotations: kwargs['help'] += type_to_str( self._annotations[variable]) + ', ' # Required/default if kwargs.get('required', False): kwargs['help'] += 'required' else: kwargs['help'] += f'default={kwargs.get("default", None)}' kwargs['help'] += ')' # Description if variable in self.class_variables: kwargs[ 'help'] += ' ' + self.class_variables[variable]['comment'] # Set other kwargs where not provided if variable in self._annotations: # Get type annotation var_type = self._annotations[variable] # If type is not explicitly provided, set it if it's one of our supported default types if 'type' not in kwargs: if var_type not in SUPPORTED_DEFAULT_TYPES: raise ValueError( f'Variable "{variable}" has type "{var_type}" which is not supported by default.\n' f'Please explicitly add the argument to the parser by writing:\n\n' f'def add_arguments(self) -> None:\n' f' self.add_argument("--{variable}", type=func, {"required=True" if kwargs["required"] else f"default={getattr(self, variable)}"})\n\n' f'where "func" maps from str to {var_type}.') # If Optional type, extract type if var_type in SUPPORTED_DEFAULT_OPTIONAL_TYPES: var_type = var_type.__args__[0] # If List type, extract type of elements in list and set nargs elif var_type in SUPPORTED_DEFAULT_COLLECTION_TYPES: var_type = var_type.__args__[0] kwargs['nargs'] = kwargs.get('nargs', '*') # If bool then set action, otherwise set type if var_type == bool: kwargs['action'] = kwargs.get( 'action', f'store_{"true" if kwargs["required"] or not kwargs["default"] else "false"}' ) else: kwargs['type'] = var_type super(Tap, self).add_argument(*name_or_flags, **kwargs)
def _add_argument(self, *name_or_flags, **kwargs) -> None: """Adds an argument to self (i.e. the super class ArgumentParser). Sets the following attributes of kwargs when not explicitly provided: - type: Set to the type annotation of the argument. - default: Set to the default value of the argument (if provided). - required: True if a default value of the argument is not provided, False otherwise. - action: Set to "store_true" if the argument is a required bool or a bool with default value False. Set to "store_false" if the argument is a bool with default value True. - nargs: Set to "*" if the type annotation is List[str], List[int], or List[float]. - help: Set to the argument documentation from the class docstring. :param name_or_flags: Either a name or a list of option strings, e.g. foo or -f, --foo. :param kwargs: Keyword arguments. """ # Get variable name variable = get_dest(*name_or_flags, **kwargs) # Get default if not specified if hasattr(self, variable): kwargs['default'] = kwargs.get('default', getattr(self, variable)) # Set required if option arg if is_option_arg(*name_or_flags) and variable != 'help': kwargs['required'] = kwargs.get('required', not hasattr(self, variable)) # Set help if necessary if 'help' not in kwargs: kwargs['help'] = '(' # Type if variable in self._annotations: kwargs['help'] += type_to_str( self._annotations[variable]) + ', ' # Required/default if kwargs.get('required', False): kwargs['help'] += 'required' else: kwargs['help'] += f'default={kwargs.get("default", None)}' kwargs['help'] += ')' # Description if variable in self.class_variables: kwargs[ 'help'] += ' ' + self.class_variables[variable]['comment'] # Set other kwargs where not provided if variable in self._annotations: # Get type annotation var_type = self._annotations[variable] # If type is not explicitly provided, set it if it's one of our supported default types if 'type' not in kwargs: # First check whether it is a literal type or a boxed literal type if is_literal_type(var_type): var_type, kwargs['choices'] = get_literals( var_type, variable) elif (get_origin(var_type) in (List, list, Set, set) and len(get_args(var_type)) > 0 and is_literal_type(get_args(var_type)[0])): var_type, kwargs['choices'] = get_literals( get_args(var_type)[0], variable) kwargs['nargs'] = kwargs.get('nargs', '*') # Handle Tuple type (with type args) by extracting types of Tuple elements and enforcing them elif get_origin(var_type) in (Tuple, tuple) and len( get_args(var_type)) > 0: loop = False types = get_args(var_type) # Don't allow Tuple[()] if len(types) == 1 and types[0] == tuple(): raise ValueError( 'Empty Tuples (i.e. Tuple[()]) are not a valid Tap type ' 'because they have no arguments.') # Handle Tuple[type, ...] if len(types) == 2 and types[1] == Ellipsis: types = types[0:1] loop = True kwargs['nargs'] = '*' else: kwargs['nargs'] = len(types) var_type = TupleTypeEnforcer(types=types, loop=loop) # To identify an Optional type, check if it's a union of a None and something else elif (is_union_type(var_type) and len(get_args(var_type)) == 2 and isinstance(None, get_args(var_type)[1]) and is_literal_type(get_args(var_type)[0])): var_type, kwargs['choices'] = get_literals( get_args(var_type)[0], variable) elif var_type not in SUPPORTED_DEFAULT_TYPES: raise ValueError( f'Variable "{variable}" has type "{var_type}" which is not supported by default.\n' f'Please explicitly add the argument to the parser by writing:\n\n' f'def add_arguments(self) -> None:\n' f' self.add_argument("--{variable}", type=func, {"required=True" if kwargs["required"] else f"default={getattr(self, variable)}"})\n\n' f'where "func" maps from str to {var_type}.') if var_type in SUPPORTED_DEFAULT_BOXED_TYPES: # If List or Set type, set nargs if var_type in SUPPORTED_DEFAULT_COLLECTION_TYPES: kwargs['nargs'] = kwargs.get('nargs', '*') # Extract boxed type for Optional, List, Set arg_types = get_args(var_type) # Set defaults type to str for Type and Type[()] if len(arg_types) == 0 or arg_types[0] == EMPTY_TYPE: var_type = str else: var_type = arg_types[0] # Handle the cases of Optional[bool], List[bool], Set[bool] if var_type == bool: var_type = boolean_type # If bool then set action, otherwise set type if var_type == bool: if self._explicit_bool: kwargs['type'] = boolean_type kwargs['choices'] = [ True, False ] # this makes the help message more helpful else: kwargs['action'] = kwargs.get( 'action', f'store_{"true" if kwargs["required"] or not kwargs["default"] else "false"}' ) else: kwargs['type'] = var_type super(Tap, self).add_argument(*name_or_flags, **kwargs)
def _add_argument(self, *name_or_flags, **kwargs) -> None: """Adds an argument to self (i.e. the super class ArgumentParser). Sets the following attributes of kwargs when not explicitly provided: - type: Set to the type annotation of the argument. - default: Set to the default value of the argument (if provided). - required: True if a default value of the argument is not provided, False otherwise. - action: Set to "store_true" if the argument is a required bool or a bool with default value False. Set to "store_false" if the argument is a bool with default value True. - nargs: Set to "*" if the type annotation is List[str], List[int], or List[float]. - help: Set to the argument documentation from the class docstring. :param name_or_flags: Either a name or a list of option strings, e.g. foo or -f, --foo. :param kwargs: Keyword arguments. """ # Set explicit bool explicit_bool = self._explicit_bool # Get variable name variable = get_argument_name(*name_or_flags) # Get default if not specified if hasattr(self, variable): kwargs['default'] = kwargs.get('default', getattr(self, variable)) # Set required if option arg if (is_option_arg(*name_or_flags) and variable != 'help' and 'default' not in kwargs and kwargs.get('action') != 'version'): kwargs['required'] = kwargs.get('required', not hasattr(self, variable)) # Set help if necessary if 'help' not in kwargs: kwargs['help'] = '(' # Type if variable in self._annotations: kwargs['help'] += type_to_str( self._annotations[variable]) + ', ' # Required/default if kwargs.get('required', False): kwargs['help'] += 'required' else: kwargs['help'] += f'default={kwargs.get("default", None)}' kwargs['help'] += ')' # Description if variable in self.class_variables: kwargs[ 'help'] += ' ' + self.class_variables[variable]['comment'] # Set other kwargs where not provided if variable in self._annotations: # Get type annotation var_type = self._annotations[variable] # If type is not explicitly provided, set it if it's one of our supported default types if 'type' not in kwargs: # Unbox Optional[type] and set var_type = type if get_origin(var_type) in OPTIONAL_TYPES: var_args = get_args(var_type) if len(var_args) > 0: var_type = get_args(var_type)[0] # If var_type is tuple as in Python 3.6, change to a typing type # (e.g., (typing.List, <class 'bool'>) ==> typing.List[bool]) if isinstance(var_type, tuple): var_type = var_type[0][var_type[1:]] explicit_bool = True # First check whether it is a literal type or a boxed literal type if is_literal_type(var_type): var_type, kwargs['choices'] = get_literals( var_type, variable) elif (get_origin(var_type) in (List, list, Set, set) and len(get_args(var_type)) > 0 and is_literal_type(get_args(var_type)[0])): var_type, kwargs['choices'] = get_literals( get_args(var_type)[0], variable) kwargs['nargs'] = kwargs.get('nargs', '*') # Handle Tuple type (with type args) by extracting types of Tuple elements and enforcing them elif get_origin(var_type) in (Tuple, tuple) and len( get_args(var_type)) > 0: loop = False types = get_args(var_type) # Don't allow Tuple[()] if len(types) == 1 and types[0] == tuple(): raise ValueError( 'Empty Tuples (i.e. Tuple[()]) are not a valid Tap type ' 'because they have no arguments.') # Handle Tuple[type, ...] if len(types) == 2 and types[1] == Ellipsis: types = types[0:1] loop = True kwargs['nargs'] = '*' else: kwargs['nargs'] = len(types) var_type = TupleTypeEnforcer(types=types, loop=loop) if get_origin(var_type) in BOXED_TYPES: # If List or Set type, set nargs if (get_origin(var_type) in BOXED_COLLECTION_TYPES and kwargs.get('action') not in {'append', 'append_const'}): kwargs['nargs'] = kwargs.get('nargs', '*') # Extract boxed type for Optional, List, Set arg_types = get_args(var_type) # Set defaults type to str for Type and Type[()] if len(arg_types) == 0 or arg_types[0] == EMPTY_TYPE: var_type = str else: var_type = arg_types[0] # Handle the cases of List[bool], Set[bool], Tuple[bool] if var_type == bool: var_type = boolean_type # If bool then set action, otherwise set type if var_type == bool: if explicit_bool: kwargs['type'] = boolean_type kwargs['choices'] = [ True, False ] # this makes the help message more helpful else: action_cond = "true" if kwargs.get( "required", False) or not kwargs["default"] else "false" kwargs['action'] = kwargs.get('action', f'store_{action_cond}') elif kwargs.get('action') not in {'count', 'append_const'}: kwargs['type'] = var_type if self._underscores_to_dashes: name_or_flags = [ name_or_flag.replace('_', '-') for name_or_flag in name_or_flags ] super(Tap, self).add_argument(*name_or_flags, **kwargs)
def _add_argument(self, *name_or_flags, **kwargs) -> None: """Adds an argument to self (i.e. the super class ArgumentParser). Sets the following attributes of kwargs when not explicitly provided: - type: Set to the type annotation of the argument. - default: Set to the default value of the argument (if provided). - required: True if a default value of the argument is not provided, False otherwise. - action: Set to "store_true" if the argument is a required bool or a bool with default value False. Set to "store_false" if the argument is a bool with default value True. - nargs: Set to "*" if the type annotation is List[str], List[int], or List[float]. - help: Set to the argument documentation from the class docstring. :param name_or_flags: Either a name or a list of option strings, e.g. foo or -f, --foo. :param kwargs: Keyword arguments. """ # Get variable name variable = get_dest(*name_or_flags, **kwargs) # Get default if not specified if hasattr(self, variable): kwargs['default'] = kwargs.get('default', getattr(self, variable)) # Set required if option arg if is_option_arg(*name_or_flags) and variable != 'help': kwargs['required'] = kwargs.get('required', not hasattr(self, variable)) # Set help if necessary if 'help' not in kwargs: kwargs['help'] = '(' # Type if variable in self._annotations: kwargs['help'] += type_to_str( self._annotations[variable]) + ', ' # Required/default if kwargs.get('required', False): kwargs['help'] += 'required' else: kwargs['help'] += f'default={kwargs.get("default", None)}' kwargs['help'] += ')' # Description if variable in self.class_variables: kwargs[ 'help'] += ' ' + self.class_variables[variable]['comment'] # Set other kwargs where not provided if variable in self._annotations: # Get type annotation var_type = self._annotations[variable] # If type is not explicitly provided, set it if it's one of our supported default types if 'type' not in kwargs: if var_type not in SUPPORTED_DEFAULT_TYPES: raise ValueError( f'Variable "{variable}" has type "{var_type}" which is not supported by default.\n' f'Please explicitly add the argument to the parser by writing:\n\n' f'def add_arguments(self) -> None:\n' f' self.add_argument("--{variable}", type=func, {"required=True" if kwargs["required"] else f"default={getattr(self, variable)}"})\n\n' f'where "func" maps from str to {var_type}.') # If Optional type, extract type if var_type in SUPPORTED_DEFAULT_OPTIONAL_TYPES: var_type = var_type.__args__[0] # If List type, extract type of elements in list and set nargs elif var_type in SUPPORTED_DEFAULT_COLLECTION_TYPES: var_type = var_type.__args__[0] kwargs['nargs'] = kwargs.get('nargs', '*') # If bool then set action, otherwise set type if var_type == bool: kwargs['action'] = kwargs.get( 'action', f'store_{"true" if kwargs["required"] or not kwargs["default"] else "false"}' ) else: kwargs['type'] = var_type super(Tap, self).add_argument(*name_or_flags, **kwargs)