diff --git a/src/argh/dto.py b/src/argh/dto.py new file mode 100644 index 0000000..6b66c7e --- /dev/null +++ b/src/argh/dto.py @@ -0,0 +1,85 @@ +""" +Data transfer objects for internal usage. +""" +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, List, Optional, Union, Type + + +class NotDefined: + """ + Specifies that an argument should not be passed to + ArgumentParser.add_argument(), even as None + """ + + +@dataclass +class ParserAddArgumentSpec: + """ + DTO, maps CLI arg(s) onto a function arg. + Ends up in ArgumentParser.add_argument(). + """ + + func_arg_name: Optional[str] # TODO: make it required (needs rearranging the logic) + cli_arg_names: List[str] + is_required: Union[bool, Type[NotDefined]] = NotDefined + default_value: Any = NotDefined + nargs: Optional[str] = None + other_add_parser_kwargs: Dict[str, Any] = field(default_factory=dict) + + # https://kislyuk.github.io/argcomplete/#specifying-completers + completer: Optional[Callable] = None + + def update(self, other: "ParserAddArgumentSpec") -> None: + for name in other.cli_arg_names: + if name not in self.cli_arg_names: + self.cli_arg_names.append(name) + + if other.is_required != NotDefined: + self.is_required = other.is_required + + if other.default_value != NotDefined: + self.default_value = other.default_value + + if other.nargs: + self.nargs = other.nargs + + if other.completer: + self.completer = other.completer + + self.other_add_parser_kwargs.update(other.other_add_parser_kwargs) + + def get_all_kwargs(self) -> Dict[str, Any]: + kwargs: Dict[str, Any] = {} + + if self.is_required != NotDefined: + kwargs["required"] = self.is_required + + if self.default_value != NotDefined: + kwargs["default"] = self.default_value + + if self.nargs: + kwargs["nargs"] = self.nargs + + return dict(kwargs, **self.other_add_parser_kwargs) + + @classmethod + def make_from_kwargs(cls, func_arg_name, cli_arg_names, parser_add_argument_kwargs: Dict[str, Any]) -> "ParserAddArgumentSpec": + """ + Constructs and returns a `ParserAddArgumentSpec` instance + according to keyword arguments according to the + `ArgumentParser.add_argument()` signature. + """ + kwargs_copy = parser_add_argument_kwargs.copy() + instance = cls( + func_arg_name=func_arg_name, + cli_arg_names=cli_arg_names, + ) + if "required" in kwargs_copy: + instance.is_required = kwargs_copy.pop("required") + if "nargs" in kwargs_copy: + instance.nargs = kwargs_copy.pop("nargs") + if "default" in kwargs_copy: + instance.default_value = kwargs_copy.pop("default") + if kwargs_copy: + instance.other_add_parser_kwargs = kwargs_copy + return instance