Python 点击​​ : make option depend on previous option

标签 python command-line-interface python-click

有没有一种惯用的方法,使用 Python Click library , 创建一个命令,其中一个选项取决于前一个选项设置的值?

一个具体的例子(我的用例)是一个命令接受类型为 click.File 的选项作为输入,还有一个指定输入流编码的编码选项:

import click

@click.command()
@click.option("--encoding", type=str, default="utf-8")
@click.option("--input",
              type=click.File("r", encoding="CAN I SET THIS DYNAMICALLY BASED ON --encoding?"))
def cli(encoding, input):
    pass

我猜它必须涉及某种使用可调用对象的延迟评估,但我不确定是否有可能考虑到当前的 Click API。

我发现我可以按照以下方式做一些事情:

import click

@click.command()
@click.pass_context
@click.option("--encoding", type=str, default="utf-8")
@click.option("--input", type=str, default="-")
def cli(ctx, encoding, input):
    input = click.File("r", encoding=encoding)(input, ctx=ctx)

但是,将选项装饰器与适用于它的语义正确的类型约束分离,并将 str 放在那里而不是作为一个虚拟对象,它在某种程度上感觉不太可读/可维护。所以如果有什么办法可以把这两者结合起来,请赐教。

建议的解决方法:

我想我可以使用 click.File 类型两次,让它在装饰器中变得懒惰,这样文件就不会真正保持打开状态,这是第一次:

@click.option("--input", type=click.File("r", lazy=True), default="-")

这在语义上感觉更令人满意,但也是多余的。

最佳答案

可以从 click.File 类继承并覆盖 .convert() 方法以允许它从上下文中收集编码值。

使用自定义类

它应该看起来像:

@click.command()
@click.option("--my_encoding", type=str, default="utf-8")
@click.option("--in_file", type=CustomFile("r", encoding_option_name="my_encoding"))
def cli(my_encoding, in_file):
    ....

CustomFile 应该允许用户为应该从中收集编码值的参数指定他们想要的任何名称,但可以有一个合理的默认值,例如“encoding”。

自定义文件类

此 CustomFile 类可与编码选项结合使用:

import click

class CustomFile(click.File):
    """
    A custom `click.File` class which will set its encoding to
    a parameter.

    :param encoding_option_name: The 'name' of the encoding parameter
    """
    def __init__(self, *args, encoding_option_name="encoding", **kwargs):
        # enforce a lazy file, so that opening the file is deferred until after
        # all of the command line parameters have been processed (--encoding
        # might be specified after --in_file)
        kwargs['lazy'] = True
        # Python 3 can use just super()
        super(CustomFile, self).__init__(*args, **kwargs)
        self.lazy_file = None
        self.encoding_option_name = encoding_option_name

    def convert(self, value, param, ctx):
        """During convert, get the encoding from the context."""
        if self.encoding_option_name not in ctx.params:
            # if the encoding option has not been processed yet, wrap its
            # convert hook so that it also retroactively modifies the encoding
            # attribute on self and self.lazy_file
            encoding_opt = [
                c for c in ctx.command.params
                if self.encoding_option_name == c.human_readable_name]
            assert encoding_opt, \
                "option '{}' not found for encoded_file".format(
                    self.encoding_option_name)

            encoding_type = encoding_opt[0].type
            encoding_convert = encoding_type.convert

            def encoding_convert_hook(*convert_args):
                encoding_type.convert = encoding_convert
                self.encoding = encoding_type.convert(*convert_args)
                self.lazy_file.encoding = self.encoding
                return self.encoding

            encoding_type.convert = encoding_convert_hook
        else:
            # if it has already been processed, just use the value
            self.encoding = ctx.params[self.encoding_option_name]

        # Python 3 can use just super()
        self.lazy_file = super(CustomFile, self).convert(value, param, ctx)
        return self.lazy_file

关于Python 点击​​ : make option depend on previous option,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35632456/

相关文章:

python - 如何在 Click 链接组中对命令列表显示进行分类?

python - 如何从元组列表中创建变量并将其设置为类?

Python:计算一组整数中所有元素之间的差异

go - 命令行标志和命令

linux - Rust非交互式标准输入

python - 从单击命令调用另一个单击命令

python - 如何在 python 中为命令行应用程序构建交互式菜单?

python - Cython 和 CPython 之间的列表理解结果不同

python - 类型错误 : context must be a dict rather than Context

batch-file - 命令提示符仅复制文件夹和子文件夹中的图像