defopt
defopt copied to clipboard
Add a registry class
I really like the fact that defopt works without mandatory decorators.
But, practically, I find myself repeatedly writing some small utility that allows using decorators to register subcommands to pass into defopt.run()
. My most recent one looks like this:
main = CLI(short={}, parsers=...)
@main.cmd('open')
def open_(target: str, *paths: str):
...
@main.cmd
def close(target: str):
...
if __name__ == '__main__':
main()
The decorator serves as an annotation about which functions are exposed as CLI subcommands, which is handy in a longer file where many of the functions are not exposed.
I think an optional registration utility like this would be a handy thing for defopt to have.
This seems reasonable modulo API bikeshedding, but I will not have the bandwidth to consider all the options at least for the coming month(s). But I'll keep this open for later.
One question, though, is how you propose to handle subcommands in this API. Perhaps foo = main.add_subcommand("foo")
and then an @foo.cmd
decorator? (perhaps that would be better named @foo.add_command
, for consistency?)
Using that pattern in Click and argparse, I find those then become a maze of indirection through a module. One alternative would be to avoid creating the intermediate objects with partially bound state and just allow the decorators to accept the path where a function will be exposed:
@main.command('project', 'new')
def new_project():
print("creating new project")
$ myproj project new
creating new project
Agreed that @main.command("foo", "bar")
is likely the most user-friendly.
Do you want to try your hand at proposing a PR? :-)