def add_arguments_to_command(cmd, fn, abbrevs=None): doc_str = fn.__doc__ or "" doc_params = parse_doc_params(doc_str) abbrevs = abbrevs or ["-h"] for var_name, default in get_var_name_and_default(fn): tp, tp_name, default, container_type = get_fn_info(fn, var_name, default) if is_pydantic(tp): # msg = f"Cannot use pydantic just yet, argument {var_name!r} (type {tp.__name__}) on cmd {cmd.prog!r}" # raise ValueError(msg) add_group(cmd, tp, fn, var_name, abbrevs) continue doc_text = doc_params.get(var_name, "") # changing the name to "no_X" in case the default is True for X, since we should set a flag to invert it # e.g. --sums becomes --no-sums if tp == bool and default == True: var_name = "no_" + var_name bool_inverted.add(var_name) default = False default_help = f"Default: {default} | " if default != "--1" else "" default = True else: if isinstance(default, Enum): default_fmt = default.name elif default == "--1": default_fmt = "" else: default_fmt = default default_help = f"Default: {default_fmt} | " if default != "--1" else "" arg_desc = f"|{tp_name}| {default_help}" + doc_text add_argument(cmd, tp, container_type, var_name, default, arg_desc, abbrevs) return abbrevs
def add_arguments_to_command(cmd, fn): doc_str = fn.__doc__ or "" arg_count = fn.__code__.co_argcount defs = fn.__defaults__ or tuple() defaults = (("--1", ) * arg_count + defs)[-arg_count:] doc_params = parse_doc_params(doc_str) abbrevs = ["-h"] for var_name, default in zip(fn.__code__.co_varnames, defaults): default_help = f"Default: {default} | " if default != "--1" else "" default_type = type( default) if default != "--1" and default is not None else None tp = fn.__annotations__.get(var_name, default_type or str) # List, Iterable, Set, Tuple container_type = False if default_type in [list, set, tuple]: for value in default: break else: value = "" container_type = default_type if "typing" not in str(tp): tp_args = ", ".join(set(type(x).__name__ for x in default)) or "str" tp_name = "1 or more of: " + tp_args tp = None else: tp_args = ", ".join(x.__name__ for x in tp.__args__) tp_name = "1 or more of: " + tp_args # tp = type(value) tp = None else: try: container_type = tp._name in [ "List", "Iterable", "Set", "Tuple" ] except AttributeError: pass if container_type: if tp.__args__ and "Union" in str(tp.__args__[0]): # cannot cast tp_arg = "str" elif tp.__args__: tp_arg = tp.__args__[0].__name__ else: tp_arg = "str" tp_name = "1 or more of: " + tp_arg else: tp_name = tp.__name__ if is_pydantic(tp): # msg = f"Cannot use pydantic just yet, argument {var_name!r} (type {tp.__name__}) on cmd {cmd.prog!r}" # raise ValueError(msg) add_group(cmd, tp, fn, var_name, abbrevs) continue arg_desc = f"|{tp_name}| {default_help}" + doc_params.get(var_name, "") add_argument(cmd, tp, container_type, var_name, default, arg_desc, abbrevs)
def test_sphinx_docstr(): inp = """ Explanation :param first: First :param second: Second """ assert parse_doc_params(inp) == { 'first': 'First', 'second': 'Second', }
def test_google_docstr(): inp = """ Args: msg (str): Human readable string describing the exception. code (:obj:`int`, optional): Error code. """ assert parse_doc_params(inp) == { 'msg': 'Human readable string describing the exception.', 'code': 'Error code.', }
def add_arguments_to_command(cmd, fn, abbrevs=None): doc_str = fn.__doc__ or "" doc_params = parse_doc_params(doc_str) abbrevs = abbrevs or ["-h"] for var_name, default in get_var_name_and_default(fn): default_type = type(default) if default != "--1" and default is not None else None tp = fn.__annotations__.get(var_name, default_type or str) # List, Iterable, Set, Tuple container_type = False if default_type in [list, set, tuple]: for value in default: break else: value = "" container_type = default_type if "typing" not in str(tp): tp_args = ", ".join(set(type(x).__name__ for x in default)) or "str" tp_name = "1 or more of: " + tp_args else: tp_args = ", ".join(x.__name__ for x in tp.__args__) tp_name = "1 or more of: " + tp_args if len(default) > 1: tp = None elif default: tp = type(list(default)[0]) elif hasattr(tp, "__args__"): tp = tp.__args__[0] else: tp = str else: try: if getattr(tp, "__origin__") == Union: tp = tp.__args__[0] container_type = CONTAINER_MAPPING.get(tp._name) except AttributeError: pass if container_type: if tp.__args__ and "Union" in str(tp.__args__[0]): # cannot cast tp_arg = "str" elif tp.__args__: tp_arg = tp.__args__[0].__name__ else: tp_arg = "str" tp_name = "0 or more of: " + tp_arg tp = tp.__args__[0] elif tp == "str": tp = str tp_name = "str" else: tp_name = tp.__name__ if is_pydantic(tp): # msg = f"Cannot use pydantic just yet, argument {var_name!r} (type {tp.__name__}) on cmd {cmd.prog!r}" # raise ValueError(msg) add_group(cmd, tp, fn, var_name, abbrevs) continue doc_text = doc_params.get(var_name, "") # changing the name to "no_X" in case the default is True for X, since we should set a flag to invert it # e.g. --sums becomes --no-sums if tp == bool and default == True: var_name = "no_" + var_name bool_inverted.add(var_name) default = False default_help = f"Default: {default} | " if default != "--1" else "" default = True else: if isinstance(default, Enum): default_fmt = default.name elif default == "--1": default_fmt = "" else: default_fmt = default default_help = f"Default: {default_fmt} | " if default != "--1" else "" arg_desc = f"|{tp_name}| {default_help}" + doc_text add_argument(cmd, tp, container_type, var_name, default, arg_desc, abbrevs) return abbrevs