Prohibir pasar varios interruptores de funciones en el clic de Python

Python click permite especificar algunas opciones de línea de comandos como “interruptores de características”. Ejemplo del official documentation:

@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
              default=True)
@click.option('--lower', 'transformation', flag_value='lower')
def info(transformation):
    click.echo(getattr(sys.platform, transformation)())

Como su nombre indica, se usa para el caso cuando hay varias alternativas para alguna función y solo se puede seleccionar una. El código anterior permite ejecutar el script como

$ test.py --upper
LINUX2
$ test.py --lower
linux2
$ test.py
LINUX2

Sin embargo, la misma secuencia de comandos permite al usuario especificar ambas opciones en la línea de comandos. Click utilizará silenciosamente la última opción especificada:

$ test.py --upper --lower
linux2

¿Hay alguna forma de forzar el clic para verificar que no se haya pasado más de una de estas opciones en la línea de comandos?

Mejor respuesta
Una forma de abordar lo que está buscando es heredar de click.Option y personalizar el analizador.

Clase personalizada:

import click


class OnceSameNameOption(click.Option):

    def add_to_parser(self, parser, ctx):

        def parser_process(value, state):
            # method to hook to the parser.process
            if self.name in state.opts:
                param_same_name = [
                    opt.opts[0] for opt in ctx.command.params
                    if isinstance(opt, OnceSameNameOption) and opt.name == self.name
                ]

                raise click.UsageError(
                    "Illegal usage: `{}` are mutually exclusive arguments.".format(
                        ', '.join(param_same_name))
                )

            # call the actual process
            self._previous_parser_process(value, state)

        retval = super(OnceSameNameOption, self).add_to_parser(parser, ctx)
        for name in self.opts:
            our_parser = parser._long_opt.get(name) or parser._short_opt.get(name)
            if our_parser:
                self._previous_parser_process = our_parser.process
                our_parser.process = parser_process
                break
        return retval

Usando la clase personalizada:

Para usar la clase personalizada, pase el parámetro cls a @ click.option () decorator como:

@click.option("--an_option", 'option-name', cls=OnceSameNameOption)

La cadena nombre-opción se usa para verificar otras invocaciones de la misma opción.

¿Como funciona esto?

Esto funciona porque hacer clic es un marco OO bien diseñado. El decorador @ click.option () generalmente crea una instancia de
Haga clic en el objeto Opción, pero permite que este comportamiento se anule con el parámetro cls. Así que es una relativa
fácil de heredar del clic. Opción en nuestra propia clase y sobrepasa los métodos deseados.

En este caso, sobrepasamos click.Option.add_to_parser () y el mono parchea el analizador para que podamos
Valide que el mismo parámetro de nombre no se haya visto antes.

Código de prueba:

@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
              cls=OnceSameNameOption, default=True)
@click.option('--lower', 'transformation', flag_value='lower',
              cls=OnceSameNameOption)
def info(transformation):
    """Show the transformed platform"""
    click.echo(getattr(sys.platform, transformation)())

if __name__ == "__main__":
    commands = (
        '--upper --lower',
        '--upper',
        '--lower',
        '',
        '--help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for cmd in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + cmd)
            time.sleep(0.1)
            info(cmd.split())

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

Resultados de la prueba:

Click Version: 6.7
Python Version: 3.6.2 (default, Jul 17 2017, 23:14:31) 
[GCC 5.4.0 20160609]
-----------
> --upper --lower
Error: Illegal usage: `--upper, --lower` are mutually exclusive arguments.
-----------
> --upper
LINUX
-----------
> --lower
linux
-----------
> 
LINUX
-----------
> --help
Usage: test.py [OPTIONS]

  Show the transformed platform

Options:
  --upper
  --lower
  --help   Show this message and exit.

Por favor indique la dirección original:Prohibir pasar varios interruptores de funciones en el clic de Python - Código de registro