I want to pass a file (netCDF data file) as first argument and an option (-v) to indicate the variable(s) to read from this file to my script.
I figured I'd need a custom callback to evaluate if this or these variable(s) is/are contained in the file. However, I'm stuck figuring out how to access the argument from within my callback method.
import click
import xarray as xr
def validate_variable(ctx, param, value):
"""Check that requested variable(s) are in netCDF file """
# HOW TO ACCESS ARGUMENT 'tile' ???
with xr.open_dataset(ctx.tile) as ds:
for v in value:
if v not in ds.data_vars:
raise click.BadParameter('Var %s not in file %s.' % (v, ctx.tile))
EPILOG = 'my script ...'
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.command(context_settings=CONTEXT_SETTINGS, epilog=EPILOG)
@click.pass_context
@click.option('--variable', '-v', multiple=True, metavar='VAR',
callback=validate_variable,
help='variable to render')
@click.argument('tile', type=click.File())
def cli(tile, variable):
main(tile, variable)
You are running into problems trying to do this in the callback, because the order in which the callbacks are invoked does not guarantee that all of the parameters you need, will have been evaluated when your callback is invoked.
One way you can do this is to override click.Command.make_context()
, and do your validation after the context construction is complete.
This function is passed a validator function and returns a custom click.Command
class.
def command_validate(validator):
class CustomClass(click.Command):
def make_context(self, *args, **kwargs):
ctx = super(CustomClass, self).make_context(*args, **kwargs)
validator(ctx)
return ctx
return CustomClass
The validator function is passed a context, and can raise a click.BadParameter
exception when the validation fails.
def validate_variables(ctx):
"""Check that requested variable(s) are in netCDF file """
value = ctx.params['variable']
tile = ctx.params['tile']
with xr.open_dataset(tile) as ds:
for v in value:
if v not in ds.data_vars:
raise click.BadParameter(
'Var %s not in file %s.' % (v, tile), ctx)
To use the custom class, pass command_validate
a validator function, and pass the return value as cls
to the command decorator like:
@click.command(cls=command_validate(validate_variables), ...OtherOptions...)
...Other config...