cmd2 icon indicating copy to clipboard operation
cmd2 copied to clipboard

Document use of docopt-based parser for option processing

Open phrrngtn opened this issue 2 years ago • 4 comments

Hi, I like to use docopt for simple scripts. I wrote an integration with Cmd2 yearas ago and have been using it at work. I only noticed recently that there is an argparser integration in Cmd2 and then led me to search for something that converts docopt to argparser and that led me to argopt.

This allows you to write Cmd2 commands with usage-style docstrings and all the completion stuff etc just works.

@hasdocopt
def do_banana(self, opts):
        '''Example programme description.
You should be able to do
    args = argopt(__doc__).parse_args()
instead of
    args = docopt(__doc__)

Usage:
    banana [options] <x> [<y>...]

Arguments:
    <x>                   A file.
    --anarg=<a>           Description here [default: 1e3:int].
    -p PAT, --patts PAT   Or [default: None:file].
    --bar=<b>             Another [default: something] should
                          auto-wrap something in quotes and assume str.
    -f, --force           Force.
'''

The implementation of the decorator is very straightforward

from argopt import argopt

def hasdocopt(f, *args, **kwargs):
    parser=argopt(f.__doc__)
    return with_argparser(parser)(f)

I have not tested this extensively but I was kind of surprised that it seems to work so well. I wanted to share the results here. Perhaps the recipe could be added to the documentation if you think it may be useful to others.

phrrngtn avatar Mar 22 '22 02:03 phrrngtn

@phrrngtn This looks pretty great. Will have to play with this some and see how it goes. Does docopt allow sub-commands like argparse?

@jayrod You might like this too

anselor avatar Mar 22 '22 03:03 anselor

No, I don't think docopt does but I did a quick search and found this https://github.com/abingham/docopt-subcommand which claims to support it.

I am not a huge fan of subcommands myself, so I tended to run a new nested Cmd2 for the odd time I wanted subcommands:

    def do_maintenance(self, argstr):
        c = Maintenance()
        c.engine = self.engine
        c.cmdloop()

This creates a top-level command, maintenance, that drops down into a Cmd2 subclass with all my maintenance commands in there. In general, this kind of things was used to bump up the logging, enables some potentially dangerous commands etc.

In any case, I find the clarity of "usage message first" authoring of options to be a sufficient win for the reduction in expressive power of rolling the argparser by hand. There is something about seeing them all there together in a single block of text that enables me to think of the options as a set and see how the individual elements relate to each other. I have to admit that I rarely -- if ever -- refactor command signatures that are expressed in a non-declarative way. YMMV.

phrrngtn avatar Mar 22 '22 13:03 phrrngtn

@phrrngtn Documenting this in an example would be useful. Please feel free to submit a PR to add an appropriate example.

tleonhardt avatar Jul 14 '22 17:07 tleonhardt

Will do. I have not been doing anything other than dayjob for several months so it may be some time.

phrrngtn avatar Jul 21 '22 16:07 phrrngtn

I'm going to close this. But @phrrngtn we would be very happy to accept a PR that adds and example to document this use case.

tleonhardt avatar Jan 28 '23 19:01 tleonhardt