-from argparse import ArgumentError
+from argparse import ArgumentError, ArgumentParser
from collections.abc import Callable
from functools import partial
from typing import Optional
args = args if args else self.args
kwargs = kwargs if kwargs else self.kwargs
return self._run(*args, **kwargs)
+
+class MainCli(ArgumentParser):
+
+ def __init__(self):
+ super().__init__()
+ self.subparsers=self.add_subparsers()
+ pass
+
+ def register_command(self, cmd: Command):
+ cmd_subparser = self.subparsers.add_parser(cmd.name)
+ _ = get_parser(cmd._run, cmd_subparser)
+
+ def dispatch(self):
+ pass
def command(fn: Callable):
""" Defines a decorator that is used to turn a function into a Command. """
-import argparse
+from argparse import ArgumentParser
from collections.abc import Callable
from functools import singledispatch
import inspect
from pathlib import Path
-from typing import Union
+from typing import Union, Optional
def get_defaults(fn: Callable)->tuple[list, dict]:
else:
raise TypeError(f"Cannot instantiate. Check the type of {prm.annotation}")
-def get_parser(fn: Callable)->argparse.ArgumentParser:
- """ Returns an argparse.ArgumentParser based on the function's signature. """
+def get_parser(fn: Callable, parser: Optional[ArgumentParser]=None)->ArgumentParser:
+ """ Returns an ArgumentParser based on the function's signature. """
sig = inspect.signature(fn)
- parser = argparse.ArgumentParser()
+ if parser is None:
+ parser = ArgumentParser()
for prm in sig.parameters.values():
# ASSUME: It's a Pythonic standard to use self, but it's convention, not rule. Beware.
if prm.name == "self" or prm.name == "cls":
--- /dev/null
+import argparse
+
+from proto import command, get_parser
+from test_command import echo_fn
+
+@command
+#def echo(arg: Stringable)->str:
+def echo(arg: str)->str:
+ return echo_fn(arg)
+
+
+if __name__ == "__main__":
+ # TODO: Standardize on "commands", not "parsers" for Main CLI interface
+ # Main CLI Ideas:
+ # cli = MainCli()
+
+ # (Defer import work - for now, focus on commands in same module)
+ # cli = MainCli(register_commands=['module.command'])
+ # cli = MainCli(register_commands_from=['module1', 'module2'])
+
+ # cli.register_subparser(cmd) # preferred, but disallows cli as an ArgumentParser
+ # The following have side effects:
+ # Command.register(cli)
+ # @command(parser=cli) or cmd = command(fn, parser=cli)
+ # _ = register_command(cli, command) # you can use this for Argparser (maybe create a method alias too)
+
+ # cli.dispatch() # dispatch = parse + run
+ # cli.dispatch(args=shlex.join("command line"))
+
+ # Start: Register_command(cli, command) - abstract subparser nonsense
+ # cli=argparse.ArgumentParser()
+ # subparsers = cli.add_subparsers()
+ # echo_subparser = subparsers.add_parser(echo.name)
+ from proto.command import MainCli
+ cli=MainCli()
+ cli.register_command(echo)
+
+ # NOTE: Uses echo._run to Command.__call__ which has a different sig.
+ # TODO: Do I want to add the parser argument?
+ # If parser is not None, it has side effects...
+ #
+# _ = get_parser(echo._run, parser=echo_subparser)
+ args = cli.parse_args()
+ # End: register_command()
+ print(echo(**vars(args)))
+
+ # NOTE: This is the single-command preference
+ #echo.parse()
+ #print(echo.run())
+
+ # NOTE: This works for kw-only signatures
+ #cli = echo.parser
+ #args = cli.parse_args()
+ #print(echo(**vars(args)))