Coverage for obj2cli/argparse.py: 68%
109 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 14:23 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-05 14:23 +0000
1import argparse
2import datetime
3import enum
4from inspect import Parameter
5from logging import getLogger
7import argparse_utils
9from .parser import Parser
11log = getLogger(__name__)
14class Argparse:
15 parser = Parser()
17 def __init__(self, positional: bool = False):
18 self.positional = positional
20 def onearg(self, arg, p):
21 optname = arg.get("name")
22 if len(optname) == 1:
23 optname = "-" + optname
24 else:
25 optname = "--" + optname
26 kwargs = {
27 "type": arg.get("type", str),
28 "default": arg.get("default"),
29 "help": arg.get("doc"),
30 "metavar": arg.get("type", str).__name__,
31 }
32 # custom action
33 if kwargs.get("type") == datetime.datetime: 33 ↛ 34line 33 didn't jump to line 34 because the condition on line 33 was never true
34 kwargs["action"] = argparse_utils.datetime_action()
35 kwargs.pop("type")
36 elif kwargs.get("type") == datetime.date: 36 ↛ 37line 36 didn't jump to line 37 because the condition on line 36 was never true
37 kwargs["action"] = argparse_utils.date_action()
38 kwargs.pop("type")
39 elif kwargs.get("type") == datetime.time: 39 ↛ 40line 39 didn't jump to line 40 because the condition on line 39 was never true
40 kwargs["action"] = argparse_utils.time_action()
41 kwargs.pop("type")
42 elif kwargs.get("type") == datetime.timedelta: 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true
43 kwargs["action"] = argparse_utils.timedelta_action()
44 kwargs.pop("type")
45 elif issubclass(kwargs.get("type"), enum.Enum):
46 kwargs["action"] = argparse_utils.enum_action(kwargs.get("type"))
47 kwargs["help"] = "/".join(kwargs.get("type").__members__.keys())
48 kwargs.pop("type")
49 elif kwargs.get("type") == dict: 49 ↛ 50line 49 didn't jump to line 50 because the condition on line 49 was never true
50 kwargs["action"] = argparse_utils.json_action()
51 kwargs.pop("type")
52 elif kwargs.get("type") == bool:
53 kwargs.pop("type")
54 kwargs.pop("metavar")
55 if kwargs.get("default", False): 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 kwargs["action"] = "store_false"
57 else:
58 kwargs["action"] = "store_true"
59 log.debug("kwargs %s", kwargs)
61 # by kind(positional/keyword)
62 if arg.get("kind") == Parameter.POSITIONAL_ONLY:
63 p.add_argument(arg.get("name"), **kwargs)
64 elif arg.get("kind") == Parameter.VAR_POSITIONAL:
65 p.add_argument(arg.get("name"), nargs="*", **kwargs)
66 elif arg.get("kind") == Parameter.VAR_KEYWORD:
67 pass
68 # p.add_argument(optname, **kwargs)
69 elif arg.get("kind") == Parameter.POSITIONAL_OR_KEYWORD:
70 if self.positional: 70 ↛ 71line 70 didn't jump to line 71 because the condition on line 70 was never true
71 p.add_argument(arg.get("name"), nargs=1, **kwargs)
72 else:
73 p.add_argument(optname, **kwargs)
74 elif arg.get("kind") == Parameter.KEYWORD_ONLY: 74 ↛ exitline 74 didn't return from function 'onearg' because the condition on line 74 was always true
75 p.add_argument(optname, **kwargs)
77 def convert_fn(self, name, data, p=None):
78 log.debug("convert_fn name=%s, data=%s", name, data)
79 if p is None: 79 ↛ 80line 79 didn't jump to line 80 because the condition on line 79 was never true
80 p = argparse.ArgumentParser(prog=name, description=data.get("doc"))
81 else:
82 p.description = data.get("doc")
83 p.set_defaults(__FUNCNAME__=name, __FUNC__=data.get("fn"))
84 log.debug("fn %s, args=%s", name, [x.get("name") for x in data.get("args", [])])
85 args = self.parser.fn_args(data)
86 log.debug("args=%s", [x.get("name") for x in args])
87 for i in args:
88 self.onearg(i, p)
89 return p
91 def convert_cls(self, data, p=None):
92 if p is None: 92 ↛ 96line 92 didn't jump to line 96 because the condition on line 92 was always true
93 name = data.get("__classmeta__", {}).get("name")
94 p = argparse.ArgumentParser(prog=name, description=data.get("doc"))
95 else:
96 p.description = data.get("doc")
97 p.set_defaults(__CLASSMETA__=data.get("__classmeta__"))
98 self.convert_fn("new", data.get("__init__"), p)
99 subs = p.add_subparsers()
100 for k, v in data.items():
101 log.debug("do1 k=%s v=%s", k, v)
102 if k == "__classmeta__":
103 log.debug("classmeta %s", v)
104 continue
105 if k == "__init__":
106 log.debug("new %s", v)
107 continue
108 if k == "doc": 108 ↛ 109line 108 didn't jump to line 109 because the condition on line 108 was never true
109 log.debug("doc %s", v)
110 continue
111 s = subs.add_parser(k, help=v.get("doc"))
112 self.convert_fn(k, v, s)
113 return p
115 def fn_arg(self, fdata, parsed):
116 kw = {}
117 pos = []
118 for ag in fdata.get("args", []):
119 name = ag.get("name")
120 if name is None:
121 log.debug("name is none")
122 continue
123 if not hasattr(parsed, name):
124 log.debug("has no %s", name)
125 continue
126 if (
127 ag.get("kind") in (Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL)
128 or ag.get("kind") == Parameter.POSITIONAL_OR_KEYWORD
129 and self.positional
130 ):
131 pos.extend(getattr(parsed, name))
132 else:
133 kw[name] = getattr(parsed, name)
134 return kw, pos
136 def split_arg(self, data, parsed):
137 carg_kw, carg_pos = self.fn_arg(data.get("__init__", {}), parsed)
138 farg_kw, farg_pos = self.fn_arg(data.get(parsed.__FUNCNAME__, {}), parsed)
139 return ((carg_pos, carg_kw), (farg_pos, farg_kw))