Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-24937: move implementation of remaining butler commands to script folder #284

Merged
merged 6 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions python/lsst/daf/butler/cli/cmd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@

__all__ = ["create", "config_dump", "config_validate"]

from .create import create
from .config_dump import config_dump
from .config_validate import config_validate

from .commands import create, config_dump, config_validate
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

import click

from ..opt import config_file_option, repo_argument
from ..utils import cli_handle_exception
from ...script import createRepo
from ..opt import config_file_option, dataset_type_option, repo_argument
from ..utils import split_commas, cli_handle_exception
from ...script import createRepo, configDump, configValidate


@click.command()
Expand All @@ -38,3 +38,29 @@
def create(*args, **kwargs):
"""Create an empty Gen3 Butler repository."""
cli_handle_exception(createRepo, *args, **kwargs)


@click.command()
@repo_argument(required=True)
@click.option("--subset", "-s", type=str,
help="Subset of a configuration to report. This can be any key in the hierarchy such as "
"'.datastore.root' where the leading '.' specified the delimiter for the hierarchy.")
@click.option("--searchpath", "-p", type=str, multiple=True,
help="Additional search paths to use for configuration overrides")
@click.option("--file", "outfile", type=click.File("w"), default="-",
help="Print the (possibly-expanded) configuration for a repository to a file, or to stdout "
"by default.")
def config_dump(*args, **kwargs):
"""Dump either a subset or full Butler configuration to standard output."""
cli_handle_exception(configDump, *args, **kwargs)


@click.command()
@repo_argument(required=True)
@click.option("--quiet", "-q", is_flag=True, help="Do not report individual failures.")
@dataset_type_option(help="Specific DatasetType(s) to validate.")
@click.option("--ignore", "-i", type=str, multiple=True, callback=split_commas,
help="DatasetType(s) to ignore for validation.")
def config_validate(*args, **kwargs):
"""Validate the configuration files for a Gen3 Butler repository."""
cli_handle_exception(configValidate, *args, **kwargs)
12 changes: 9 additions & 3 deletions python/lsst/daf/butler/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import click
import io
import os
import traceback
from unittest.mock import MagicMock

from ..core.utils import iterable
Expand Down Expand Up @@ -120,7 +122,7 @@ def split_kv(context, param, values, separator="="):
Raised if the separator is not found in an entry, or if duplicate keys
are encountered.
"""
if "," == separator or " " == separator:
if separator in (",", " "):
raise RuntimeError(f"'{separator}' is not a supported separator for key-value pairs.")
vals = split_commas(context, param, values)
ret = {}
Expand Down Expand Up @@ -181,5 +183,9 @@ def cli_handle_exception(func, *args, **kwargs):
return
try:
return func(*args, **kwargs)
except Exception as err:
raise click.ClickException(err)
except Exception:
msg = io.StringIO()
msg.write("An error occurred during command execution:\n")
traceback.print_exc(file=msg)
msg.seek(0)
raise click.ClickException(msg.read())
2 changes: 2 additions & 0 deletions python/lsst/daf/butler/script/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from .createRepo import createRepo
from .configDump import configDump
from .configValidate import configValidate
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,36 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import click
from .. import ButlerConfig

from ... import ButlerConfig
from ..opt import repo_argument

def configDump(repo, subset, searchpath, outfile):
"""Dump either a subset or full Butler configuration to standard output.

@click.command()
@repo_argument(required=True)
@click.option("--subset", "-s", type=str,
help="Subset of a configuration to report. This can be any key in the hierarchy such as "
"'.datastore.root' where the leading '.' specified the delimiter for the hierarchy.")
@click.option("--searchpath", "-p", type=str, multiple=True,
help="Additional search paths to use for configuration overrides")
@click.option("--file", "outfile", type=click.File("w"), default="-",
help="Print the (possibly-expanded) configuration for a repository to a file, or to stdout "
"by default.")
def config_dump(repo, subset, searchpath, outfile):
"""Dump either a subset or full Butler configuration to standard output."""
config = ButlerConfig(repo, searchPaths=searchpath)
Parameters
----------
repo : `str`
URI to the location to create the repo.
subset : `str`
Subset of a configuration to report. This can be any key in the
hierarchy such as '.datastore.root' where the leading '.' specified the
delimiter for the hierarchy.
searchpath : `str`
Additional search paths to use for configuration overrides
outfile : file-like object
File to which the configuration should be printed.

Raises
------
KeyError
If a subset is specified but does not exist in the configuration.
AttributeError
If there is an issue dumping the configuration.
"""
config = ButlerConfig(repo, searchPaths=searchpath)
if subset is not None:
try:
config = config[subset]
except KeyError:
raise click.ClickException(f"{subset} not found in config at {repo}")

try:
config.dump(outfile)
except AttributeError:
print(config, file=outfile)
raise KeyError(f"{subset} not found in config at {repo}")
config.dump(outfile)
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,29 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import click
from .. import Butler

from ... import Butler, ValidationError
from ..opt import dataset_type_option, repo_argument
from ..utils import split_commas

def configValidate(repo, quiet, dataset_type, ignore):
"""Validate the configuration files for a Gen3 Butler repository.

@click.command()
@click.pass_context
@repo_argument(required=True)
@click.option("--quiet", "-q", is_flag=True, help="Do not report individual failures.")
@dataset_type_option(help="Specific DatasetType(s) to validate.")
@click.option("--ignore", "-i", type=str, multiple=True, callback=split_commas,
help="DatasetType(s) to ignore for validation.")
def config_validate(ctx, repo, quiet, dataset_type, ignore):
"""Validate the configuration files for a Gen3 Butler repository."""
Parameters
----------
repo : `str`
URI to the location to create the repo.
quiet : `bool`
Do not report individual failures if True.
dataset_type : [`str`]
Specific DatasetTypes to validate.
ignore : [`str`]
"DatasetTypes to ignore for validation."

Raises
------
ValidationError
If a configuration fails validation.
"""
logFailures = not quiet
butler = Butler(config=repo)
try:
butler.validateConfiguration(logFailures=logFailures, datasetTypeNames=dataset_type, ignore=ignore)
except ValidationError:
ctx.exit(1)
click.echo("No problems encountered with configuration.")
butler.validateConfiguration(logFailures=logFailures, datasetTypeNames=dataset_type, ignore=ignore)
print("No problems encountered with configuration.")
16 changes: 8 additions & 8 deletions tests/test_cliCmdConfigDump.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ def test_stdout(self):
runner = click.testing.CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(butler.cli, ["create", "here"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")

# test dumping to stdout:
result = runner.invoke(butler.cli, ["config-dump", "here"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
# check for some expected keywords:
cfg = yaml.safe_load(result.stdout)
self.assertIn("composites", cfg)
Expand All @@ -58,9 +58,9 @@ def test_file(self):
runner = click.testing.CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(butler.cli, ["create", "here"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
result = runner.invoke(butler.cli, ["config-dump", "here", "--file=there"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
# check for some expected keywords:
with open("there", "r") as f:
cfg = yaml.safe_load(f)
Expand All @@ -73,9 +73,9 @@ def test_subset(self):
runner = click.testing.CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(butler.cli, ["create", "here"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
result = runner.invoke(butler.cli, ["config-dump", "here", "--subset", "datastore"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
cfg = yaml.safe_load(result.stdout)
# count the keys in the datastore config
self.assertIs(len(cfg), 7)
Expand All @@ -91,11 +91,11 @@ def test_invalidSubset(self):
runner = click.testing.CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(butler.cli, ["create", "here"])
self.assertEqual(result.exit_code, 0, result.stdout)
self.assertEqual(result.exit_code, 0, f"output: {result.output} exception: {result.exception}")
# test dumping to stdout:
result = runner.invoke(butler.cli, ["config-dump", "here", "--subset", "foo"])
self.assertEqual(result.exit_code, 1)
self.assertEqual(result.output, "Error: foo not found in config at here\n")
self.assertIn("Error: 'foo not found in config at here'", result.output)


if __name__ == "__main__":
Expand Down