Python Click: make option depends on previous option

Is there an idiomatic way using the Python Click library to create a command in which one parameter depends on the value set by the previous option?

A specific example (my use case) is that the command accepts a click.File type as an input parameter, as well as an encoding parameter that defines the encoding of the input stream:

 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 

I suppose that would have to include some kind of deferred evaluation using the called one, but I'm not sure if this is possible even using the current Click API.

I realized that I can do something in the following lines:

 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) 

But it somehow seems less readable / supported to separate the option decoder from the semantically correct type constraint that applies to it, and instead put str instead as a dummy. Therefore, if there is a way to keep these two together, please enlighten me.

Proposed Solution:

I think I could use the type click.File twice, making it lazy in the decorator so that the file is not actually open, for the first time:

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

It feels semantically more satisfying, but also redundant.

+5
source share
1 answer

You can inherit from the click.File class and override the .convert() method so that it can collect the encoding value from the context.

Using a custom class

It should look something like this:

 @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 should allow the user to specify whatever name they want for the parameter from which the encoding value should be collected, but there may be a reasonable default value, such as "encoding".

Custom file class

This CustomFile class can be used in conjunction with the encoding option:

 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 
+2
source

Source: https://habr.com/ru/post/1243865/


All Articles