Skip to content

Commit

Permalink
Merge branch 'tickets/DM-32986'
Browse files Browse the repository at this point in the history
  • Loading branch information
n8pease committed Dec 17, 2021
2 parents d48bc30 + 4725cba commit a73f8d6
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 1 deletion.
2 changes: 2 additions & 0 deletions doc/changes/DM-32986.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
In the butler presets file, use option names that match the butler CLI command option names (without leading dashes).
Fail if option names used in the presets file do not match options for the current butler command.
40 changes: 39 additions & 1 deletion python/lsst/daf/butler/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,17 +733,55 @@ def yaml_presets(ctx, param, value):
----------
ctx : `click.context`
The context for the click operation. Used to extract the subcommand
name.
name and translate option & argument names.
param : `str`
The parameter name.
value : `object`
The value of the parameter.
"""

def _name_for_option(ctx: click.Context, option: str) -> str:
"""Use a CLI option name to find the name of the argument to the
command function.
Parameters
----------
ctx : `click.Context`
The context for the click operation.
option : `str`
The option/argument name from the yaml file.
Returns
-------
name : str
The name of the argument to use when calling the click.command
function, as it should appear in the `ctx.default_map`.
Raises
------
RuntimeError
Raised if the option name from the yaml file does not exist in the
command parameters. This catches misspellings and incorrect useage
in the yaml file.
"""
for param in ctx.command.params:
# Remove leading dashes: they are not used for option names in the
# yaml file.
if option in [opt.lstrip("-") for opt in param.opts]:
return param.name
raise RuntimeError(f"'{option}' is not a valid option for {ctx.info_name}")

ctx.default_map = ctx.default_map or {}
cmd_name = ctx.info_name
if value:
try:
overrides = _read_yaml_presets(value, cmd_name)
options = list(overrides.keys())
for option in options:
name = _name_for_option(ctx, option)
if name == option:
continue
overrides[name] = overrides.pop(option)
except Exception as e:
raise click.BadOptionUsage(param.name, f"Error reading overrides file: {e}", ctx)
# Override the defaults for this subcommand
Expand Down
45 changes: 45 additions & 0 deletions tests/test_cliCmdConfigDump.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@
"""Unit tests for daf_butler CLI config-dump command.
"""

import click
import os.path
import os
import unittest
import yaml

from lsst.daf.butler.cli import butler
from lsst.daf.butler.cli.cmd import config_dump
from lsst.daf.butler.cli.opt import options_file_option
from lsst.daf.butler.cli.utils import clickResultMsg, LogCliRunner
from lsst.daf.butler.tests import CliCmdTestBase


TESTDIR = os.path.abspath(os.path.dirname(__file__))


Expand Down Expand Up @@ -146,6 +150,47 @@ def test_presets(self):
self.assertNotIn("formatters", cfg)
self.assertIn("managers", cfg)

configfile = "overrides.yaml"
outfile = "repodef.yaml"
# Check that a misspelled command option causes an error:
with open(configfile, "w") as f:
f.write(yaml.dump({"config-dump": {"fil": outfile}}))
result = self.runner.invoke(butler.cli, ["config-dump", "here", f"-@{configfile}"])
self.assertNotEqual(result.exit_code, 0, clickResultMsg(result))

# Check that an option that declares a different command argument
# name is mapped correctly.
# Note that the option `config-dump --file`
# becomes the `outfile` argument in `def config_dump(..., outfile)`
# and we use the option name "file" in the presets file.
with open(configfile, "w") as f:
f.write(yaml.dump({"config-dump": {"file": outfile}}))
result = self.runner.invoke(butler.cli, ["config-dump", "here", f"-@{configfile}"])
self.assertEqual(result.exit_code, 0, clickResultMsg(result))
self.assertTrue(os.path.exists(outfile))

def test_presetsDashedName(self):
"""Test file overrides when the option has a dash in its name.
"""

# Instead of using `butler config-dump` as we do in other tests,
# create a small command for testing, because config-dump does
# not have any options with dashes in the name.
@click.command()
@click.option("--test-option")
@options_file_option()
def cmd(test_option):
print(test_option)

configfile = "overrides.yaml"
val = "foo"
with self.runner.isolated_filesystem():
with open(configfile, "w") as f:
f.write(yaml.dump({"cmd": {"test-option": val}}))
result = self.runner.invoke(cmd, ["-@", configfile])
self.assertEqual(result.exit_code, 0, clickResultMsg(result))
self.assertTrue(val in result.output)


if __name__ == "__main__":
unittest.main()

0 comments on commit a73f8d6

Please sign in to comment.