Last active
April 19, 2022 04:50
-
-
Save hr3lxphr6j/26826f722dd63dbe3e4e2e51bc512598 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from argparse import ArgumentParser, Namespace | |
from functools import partial | |
from inspect import Parameter, signature | |
from typing import Callable | |
class Cli: | |
class Arg: | |
def __init__(self, *args, **kwargs) -> None: | |
self.args = args | |
self.kwargs = kwargs | |
_root_parser = None | |
_sps = None | |
__root_func = None | |
@staticmethod | |
def wrapper(fn: Callable = None, sub_command: str = None, root_parser: bool = False, **kwargs): | |
if fn is None: | |
return partial(Cli.wrapper, sub_command=sub_command, root_parser=root_parser, **kwargs) | |
if not Cli._root_parser: | |
Cli._root_parser = ArgumentParser(**(kwargs if root_parser else {})) | |
if not root_parser: | |
if not Cli._sps: | |
Cli._sps = Cli._root_parser.add_subparsers() | |
parser = Cli._sps.add_parser(sub_command or fn.__name__, **kwargs) | |
else: | |
if Cli.__root_func: | |
raise RuntimeError('root parser already set') | |
parser = Cli._root_parser | |
Cli.__root_func = fn | |
for k, v in signature(fn).parameters.items(): | |
if v.default and isinstance(v.default, Cli.Arg): | |
_args = v.default.args | |
_kwargs = v.default.kwargs | |
if _args and _args[0].startswith('-'): | |
_kwargs['dest'] = k | |
if not _args: | |
_args = ['--' + k.replace('_', '-')] | |
if 'type' not in _kwargs and 'action' not in _kwargs and isinstance(v.annotation, Callable): | |
_kwargs['type'] = v.annotation | |
else: | |
_args = ['--' + k.replace('_', '-')] | |
_kwargs = { | |
'type': v.annotation if isinstance(v.annotation, Callable) else None, | |
'default': str(v.default) if v.default and v.default != Parameter.empty else None, | |
'required': v.default == Parameter.empty, | |
'help': f'{k}, default: {str(v.default)}' if v.default and v.default != Parameter.empty else None, | |
'dest': k | |
} | |
parser.add_argument(*_args, **_kwargs) | |
if not parser.get_default('_func'): | |
parser.set_defaults(_func=fn) | |
return partial(fn, | |
**{k: v if not isinstance(v.default, Cli.Arg) else v.default.kwargs.get('default') for k, v in | |
signature(fn).parameters.items() if v.default != Parameter.empty}) | |
@staticmethod | |
def parse(*args, **kwargs) -> Namespace: | |
return Cli._root_parser.parse_args(*args, **kwargs) | |
@staticmethod | |
def run(*args, **kwargs) -> None: | |
args = Cli.parse(*args, **kwargs) | |
if '_func' not in args: | |
Cli._root_parser.print_help() | |
exit(1) | |
args._func(**{k: v for k, v in args.__dict__.items() if k != '_func'}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example