defopt icon indicating copy to clipboard operation
defopt copied to clipboard

Add a registry class

Open lordmauve opened this issue 3 years ago • 4 comments

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.

lordmauve avatar Feb 21 '22 12:02 lordmauve

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.

anntzer avatar Feb 21 '22 15:02 anntzer

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?)

anntzer avatar Mar 12 '22 22:03 anntzer

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

lordmauve avatar Apr 07 '22 16:04 lordmauve

Agreed that @main.command("foo", "bar") is likely the most user-friendly.

Do you want to try your hand at proposing a PR? :-)

anntzer avatar Apr 07 '22 17:04 anntzer