Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)