5
votes

I am working on a small command line tool using Python 2 and click. My tool either needs to write a value, read it, or do not change it. It would be nice if I could do the following:

mytool --r0=0xffff..........Set value r0 to 0xffff
mytool --r0......................Read value r0
mytool...............................Don't do anything with r0

Based on the documentation, it doesn't seem possible, but I could have missed it. So is it possible or do I have to find a different approach?

1
@click.option('--count', default=1, help='Number of greetings.') - Koga
@Koga Yeah, I had the same thought, but I was wondering if I could do something like that without using default - flashburn
Why would you not want to use default? - Koga
@Koga The tool is to read/write register values in hardware. So if I give it a default value, the meaning would be set register to a value of 1. In other words calling just mytool would be equivalent to mytool --r0=1, which means set register 0 to 1 which is not my intent. - flashburn
Haven't worked with click, but could you use a non-integer default? What I'm not sure about is how to do nothing when it's not specified, but something when it's specified bare. - mwchase

1 Answers

2
votes

One way to solve this problem is to introduce another parameter named r0_set. And then to preserve the desired command line, we can inherit from click.Command and over ride parse_args to turn the user entered r0=0xffff into r0_set=0xffff

Code:

class RegisterReaderOption(click.Option):
    """ Mark this option as getting a _set option """
    register_reader = True

class RegisterWriterOption(click.Option):
    """ Fix the help for the _set suffix """
    def get_help_record(self, ctx):
        help = super(RegisterWriterOption, self).get_help_record(ctx)
        return (help[0].replace('_set ', '='),) + help[1:]

class RegisterWriterCommand(click.Command):
    def parse_args(self, ctx, args):
        """ Translate any opt= to opt_set= as needed """
        options = [o for o in ctx.command.params
                   if getattr(o, 'register_reader', None)]
        prefixes = {p for p in sum([o.opts for o in options], [])
                    if p.startswith('--')}
        for i, a in enumerate(args):
            a = a.split('=')
            if a[0] in prefixes and len(a) > 1:
                a[0] += '_set'
                args[i] = '='.join(a)

        return super(RegisterWriterCommand, self).parse_args(ctx, args)

Test Code:

@click.command(cls=RegisterWriterCommand)
@click.option('--r0', cls=RegisterReaderOption, is_flag=True,
              help='Read the r0 value')
@click.option('--r0_set', cls=RegisterWriterOption,
              help='Set the r0 value')
def cli(r0, r0_set):
    click.echo('r0: {}  r0_set: {}'.format(r0, r0_set))

cli(['--r0=0xfff', '--r0'])
cli(['--help'])

Results:

r0: True  r0_set: 0xfff

Usage: test.py [OPTIONS]

Options:
  --r0       Read the r0 value
  --r0=TEXT  Set the r0 value
  --help     Show this message and exit.