def _argument_to_argparse_input(arg): # type: (Any) -> Tuple[List, Dict[str, Any]] add_argument_kwargs = {"help": arg.description} if arg.positional: add_argument_args = [arg.name] if arg.extra_names: msg = "Aliases are not yet supported for positional arguments @ {}".format( arg.name) raise ValueError(msg) if arg.default_value_set: msg = ("Positional arguments with default values are " "not supported @ {}".format(arg.name)) raise ValueError(msg) else: add_argument_args = [ transform_argument_name(x) for x in ([arg.name] + arg.extra_names) ] add_argument_kwargs["default"] = arg.default_value add_argument_kwargs["required"] = not arg.default_value_set argument_type = (arg.type if not is_optional_type(arg.type) else get_first_type_argument(arg.type)) if argument_type in [int, float, str]: add_argument_kwargs["type"] = argument_type add_argument_kwargs["metavar"] = str(argument_type.__name__).upper() elif argument_type == bool or arg.default_value is False: add_argument_kwargs["action"] = "store_true" elif arg.default_value is True: add_argument_kwargs["action"] = "store_false" elif is_mapping_type(argument_type): add_argument_kwargs["type"] = _parse_dict(argument_type) add_argument_kwargs["metavar"] = "DICT[{}: {}]".format( *get_dict_kv_arg_type_as_str(argument_type)) elif is_iterable_type(argument_type): add_argument_kwargs["type"] = get_first_type_argument(argument_type) add_argument_kwargs["nargs"] = "+" add_argument_kwargs["metavar"] = "{}".format( get_list_arg_type_as_str(argument_type)) else: add_argument_kwargs["type"] = argument_type if arg.choices: add_argument_kwargs["choices"] = arg.choices add_argument_kwargs["metavar"] = "{{{}}}".format(",".join( map(str, arg.choices))) if arg.positional and "metavar" in add_argument_kwargs: add_argument_kwargs["metavar"] = "{}<{}>".format( arg.name, add_argument_kwargs["metavar"]) return add_argument_args, add_argument_kwargs
def get_dict_kv_arg_type_as_str(tp): """ This takes a type (typing.Mapping[str, int]) and returns a tuple (key_type, value_type) that contains string representations of the type arguments, or "any" if it's not defined """ assert is_mapping_type(tp), f"{tp} is not a mapping type" args = getattr(tp, "__args__", None) key_type = "any" value_type = "any" if args and len(args) >= 2: key_type = getattr(args[0], "__name__", str(args[0])) value_type = getattr(args[1], "__name__", str(args[1])) return key_type, value_type
def _build_simple_value(string, tp): if not tp or issubclass_(tp, str): return string elif is_mapping_type(tp): entries = (re.split(r"\s*[:=]\s*", entry, maxsplit=1) for entry in string.split(";")) if is_dict_value_iterable(tp): entries = ((k, re.split(r"\s*,\s*", v)) for k, v in entries) return {k.strip(): v for k, v in entries} elif is_tuple_type(tp): return tuple(item for item in string.split(",")) elif is_iterable_type(tp): return [item for item in string.split(",")] else: return string
def get_typing_function(tp): func = None # TypeVars are a problem as they can defined multiple possible types. # While a single type TypeVar is somewhat useless, no reason to deny it # though if is_typevar(tp): if len(tp.__constraints__) == 0: # Unconstrained TypeVars may come from generics func = _identity_function elif len(tp.__constraints__) == 1: assert not NEW_TYPING, "Python 3.7+ forbids single constraint for `TypeVar'" func = get_typing_function(tp.__constraints__[0]) else: raise ValueError( "Cannot resolve typing function for TypeVar({constraints}) " "as it declares multiple types".format(constraints=", ".join( getattr(c, "_name", c.__name__) for c in tp.__constraints__))) elif tp == typing.Any: func = _identity_function elif issubclass_(tp, str): func = str elif is_mapping_type(tp): func = _apply_dict_type elif is_tuple_type(tp): func = _apply_tuple_type elif is_iterable_type(tp): func = _apply_list_type elif is_optional_type(tp): func = _apply_optional_type elif callable(tp): func = tp else: raise ValueError( 'Cannot find a function to apply type "{}"'.format(tp)) args = getattr(tp, "__args__", None) if args: # this can be a Generic type from the typing module, like # List[str], Mapping[int, str] and so on. In that case we need to # also deal with the generic typing args_types = [get_typing_function(arg) for arg in args] func = _partial_builder(args_types)(func) return func
def is_dict_value_iterable(tp): assert is_mapping_type(tp), f"{tp} is not a mapping type" args = getattr(tp, "__args__", None) if args and len(args) == 2: return is_iterable_type(args[1]) return False