4 from typing import Callable, Any
5 from lib.utils import Schema
7 def select(val, _if, _else):
8 return _if if val else _else
10 def get_config_name(name):
11 return f"CONFIG_{name.upper()}"
13 def cmd(name, *alias):
14 def __cmd(fn: Callable):
15 fn.__annotations__["__CMD__"] = True
16 fn.__annotations__["__NAME__"] = name
17 fn.__annotations__["__ALIAS__"] = [*alias]
21 def imply_schema(fn: Callable):
22 type_map = inspect.get_annotations(fn)
25 for arg in inspect.getargs(fn.__code__).args:
32 arg_list.append((arg, t))
34 return Schema([x[1] for x in arg_list]), arg_list
36 class ShconfigException(Exception):
37 def __init__(self, *args):
38 super().__init__(*args)
41 return str(self.args[0])
44 def __init__(self, body: Callable):
45 self.name = body.__annotations__["__NAME__"]
46 self.alias = body.__annotations__["__ALIAS__"]
47 self.help = inspect.getdoc(body)
48 self.help = textwrap.dedent(self.help if self.help else "")
50 schema, args = imply_schema(body)
51 self.argstr = ' '.join(
52 [f'<{n.upper()}: {t.__name__}>' for n, t in args])
55 self.__argtype = schema
57 def match_name(self, name):
58 return self.name == name or name in self.alias
60 def try_invoke(self, *args):
61 t_args = [self.__type_mapper(x) for x in args]
62 if self.__argtype != t_args:
63 raise ShconfigException(
64 f"invalid parameter ({args}), expect: ({self.argstr})")
68 def __type_mapper(self, strtype):
69 if strtype in ['True', 'False']:
71 if strtype in ['y', 'n']:
72 return bool(strtype == 'y')
74 try: return int(strtype)
81 *[f"{name} {self.argstr}" for name in self.alias + [self.name]],
82 textwrap.indent(self.help, '\t')
89 fns = inspect.getmembers(self,
90 lambda p: isinstance(p, Callable))
92 if not hasattr(fn, "__annotations__"):
94 if "__CMD__" not in fn.__annotations__:
97 self._cmd_map.append(Executor(fn))
99 def call(self, name, *args):
100 for exe in self._cmd_map:
101 if exe.match_name(name):
102 exe.try_invoke(*args)
105 raise ShconfigException(
106 f"command not found {name}")