Coverage for obj2cli/argparse.py: 68%

109 statements  

« 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 

6 

7import argparse_utils 

8 

9from .parser import Parser 

10 

11log = getLogger(__name__) 

12 

13 

14class Argparse: 

15 parser = Parser() 

16 

17 def __init__(self, positional: bool = False): 

18 self.positional = positional 

19 

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) 

60 

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) 

76 

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 

90 

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 

114 

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 

135 

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))