在 click.Choice 中使用数字标识符进行值选择

Ade*_*mad 5 python-click

点击包允许从使用所述列表中选择一个值的范围click.Choice方法。

在我的情况下,这些值是相对较长的字符串,因此使用:

choice_names = [u'Vulnerable BMC (IPMI)', u'IoT Vulnerability', u'SMBv1', u'BadHTTPStatus', u'Compromised']

@click.option('--category', prompt='\nPlease enter the category of incident.\n\n -- Options:\n{}\n\n'.format(
    format_choices(choice_names)), type=click.Choice(choice_names))
Run Code Online (Sandbox Code Playgroud)

将列出的值如下:

-> Vulnerable BMC (IPMI)
-> IoT Vulnerability
-> SMBv1
-> BadHTTPStatus
-> Compromised
Run Code Online (Sandbox Code Playgroud)

这需要用户输入完整的字符串,不方便。Click 是否提供仅使用数字标识符来选择值的功能?因此,上述选项可以列为:

-> Vulnerable BMC (IPMI) [1]
-> IoT Vulnerability [2]
-> SMBv1 [3]
-> BadHTTPStatus [4]
-> Compromised [5]
Run Code Online (Sandbox Code Playgroud)

并且要选择第一个选项,用户需要输入1。这可以通过定义自定义验证函数来实现,但我找不到 Click 提供的任何现有功能。

M.V*_*lee 7

我想出了这个:

class ChoiceOption(click.Option):
    def __init__(self, param_decls=None, **attrs):
        click.Option.__init__(self, param_decls, **attrs)
        if not isinstance(self.type, click.Choice):
            raise Exception('ChoiceOption type arg must be click.Choice')

        if self.prompt:
            prompt_text = '{}:\n{}\n'.format(
                self.prompt,
                '\n'.join(f'{idx: >4}: {c}' for idx, c in enumerate(self.type.choices, start=1))
            )
            self.prompt = prompt_text

    def process_prompt_value(self, ctx, value, prompt_type):
        if value is not None:
            index = prompt_type(value, self, ctx)
            return self.type.choices[index - 1]

    def prompt_for_value(self, ctx):
        # Calculate the default before prompting anything to be stable.
        default = self.get_default(ctx)

        prompt_type = click.IntRange(min=1, max=len(self.type.choices))
        return click.prompt(
            self.prompt, default=default, type=prompt_type,
            hide_input=self.hide_input, show_choices=False,
            confirmation_prompt=self.confirmation_prompt,
            value_proc=lambda x: self.process_prompt_value(ctx, x, prompt_type))

@click.command()
@click.option('--hash-type', prompt='Hash', type=click.Choice(['MD5', 'SHA1'], case_sensitive=False), cls=ChoiceOption)
def cli(**kwargs):
    print(kwargs)
Run Code Online (Sandbox Code Playgroud)

结果:

> cli --help
Usage: cli [OPTIONS]                          

Options:                                           
  --hash-type [MD5|SHA1]                           
  --help                  Show this message and exit.

> cli --hash-type MD5
{'hash_type': 'MD5'}

> cli
Hash:                                              
  1: MD5                                          
  2: SHA1                                         
: 4                                                
Error: 4 is not in the valid range of 1 to 2.      
Hash:                                              
  1: MD5                                          
  2: SHA1                                         
: 2                                                
{'hash_type': 'SHA1'}
Run Code Online (Sandbox Code Playgroud)

编辑 2020 年 5 月 25 日:我最近遇到了问题并将其与click

> cli --help
Usage: cli [OPTIONS]                          

Options:                                           
  --hash-type [MD5|SHA1]                           
  --help                  Show this message and exit.

> cli --hash-type MD5
{'hash_type': 'MD5'}

> cli
Hash:                                              
  1: MD5                                          
  2: SHA1                                         
: 4                                                
Error: 4 is not in the valid range of 1 to 2.      
Hash:                                              
  1: MD5                                          
  2: SHA1                                         
: 2                                                
{'hash_type': 'SHA1'}
Run Code Online (Sandbox Code Playgroud)

ASCII 广播


Ade*_*mad 1

由于 Click 似乎没有提供此类功能,因此这个自定义验证函数满足了目的:

def validate_choice(ctx, param, value):
    # Check if the passed value is an integer.
    try:
        index = int(value) - 1
        # Return the value at the given index.
        try:
            return choice_names[index]
        # If the index does not exist.
        except IndexError:
            click.echo('Please select a valid index.')
    # If the value is of a different type, for example, String.
    except (TypeError, ValueError):
        # Return the value if it exists in the list of choices.
        if value in choice_names:
            return value
        else:
            click.echo('Please select a valid value from the choices {}.'.format(choice_names))

    # Prompt the user for an input.
    value = click.prompt(param.prompt)
    return validate_choice(ctx, param, value)

@click.option('--category', prompt='\nPlease enter the category.\n\n -- Options:\n{}\n\n'.format(choice_names),
              help='Category of the incident', callback=validate_category)

Run Code Online (Sandbox Code Playgroud)

这允许用户通过输入选项名称或输入索引值来选择选项。如果输入了无效值,系统会再次提示用户输入。