| import os, sys, subprocess, inspect |
| from dataclasses import dataclass |
| from typing import Any |
| from argparse import ArgumentParser |
|
|
|
|
| @dataclass |
| class Param(): |
| "A parameter in a function used in `anno_parser` or `call_parse`" |
| help:str=None |
| type:type=None |
| opt:bool=True |
| action:str=None |
| nargs:str=None |
| const:str=None |
| choices:str=None |
| required:bool=None |
|
|
| @property |
| def pre(self): return '--' if self.opt else '' |
| @property |
| def kwargs(self): return {k:v for k,v in self.__dict__.items() |
| if v is not None and k!='opt'} |
|
|
| def anno_parser(func): |
| "Look at params (annotated with `Param`) in func and return an `ArgumentParser`" |
| p = ArgumentParser(description=func.__doc__) |
| for k,v in inspect.signature(func).parameters.items(): |
| param = func.__annotations__.get(k, Param()) |
| kwargs = param.kwargs |
| if v.default != inspect.Parameter.empty: kwargs['default'] = v.default |
| p.add_argument(f"{param.pre}{k}", **kwargs) |
| return p |
|
|
| def call_parse(func): |
| "Decorator to create a simple CLI from `func` using `anno_parser`" |
| name = inspect.currentframe().f_back.f_globals['__name__'] |
| if name == "__main__": |
| args = anno_parser(func).parse_args() |
| func(**args.__dict__) |
| else: return func |
|
|
| def call_plac(f): |
| "Decorator to create a simple CLI from `func` using `plac`" |
| name = inspect.currentframe().f_back.f_globals['__name__'] |
| if name == '__main__': |
| import plac |
| res = plac.call(f) |
| if callable(res): res() |
| else: return f |
|
|
|
|