Coverage for python/lsst/ctrl/mpexec/cli/cmd/commands.py: 68%
90 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-11 01:24 -0700
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-11 01:24 -0700
1# This file is part of ctrl_mpexec.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22import sys
23from functools import partial
24from typing import Any
26import click
27import lsst.pipe.base.cli.opt as pipeBaseOpts
28from lsst.ctrl.mpexec.showInfo import ShowInfo
29from lsst.daf.butler.cli.opt import config_file_option, config_option, confirm_option, options_file_option
30from lsst.daf.butler.cli.utils import MWCtxObj, catch_and_exit, option_section, unwrap
32from .. import opt as ctrlMpExecOpts
33from .. import script
34from ..script import confirmable
35from ..utils import _ACTION_CONFIG, _ACTION_CONFIG_FILE, PipetaskCommand, makePipelineActions
37epilog = unwrap(
38 """Notes:
40--task, --delete, --config, --config-file, and --instrument action options can
41appear multiple times; all values are used, in order left to right.
43FILE reads command-line options from the specified file. Data may be
44distributed among multiple lines (e.g. one option per line). Data after # is
45treated as a comment and ignored. Blank lines and lines starting with # are
46ignored.)
47"""
48)
51def _collectActions(ctx: click.Context, **kwargs: Any) -> dict[str, Any]:
52 """Extract pipeline building options, replace them with PipelineActions,
53 return updated `kwargs`.
55 Notes
56 -----
57 The pipeline actions (task, delete, config, config_file, and instrument)
58 must be handled in the order they appear on the command line, but the CLI
59 specification gives them all different option names. So, instead of using
60 the individual action options as they appear in kwargs (because
61 invocation order can't be known), we capture the CLI arguments by
62 overriding `click.Command.parse_args` and save them in the Context's
63 `obj` parameter. We use `makePipelineActions` to create a list of
64 pipeline actions from the CLI arguments and pass that list to the script
65 function using the `pipeline_actions` kwarg name, and remove the action
66 options from kwargs.
67 """
68 for pipelineAction in (
69 ctrlMpExecOpts.task_option.name(),
70 ctrlMpExecOpts.delete_option.name(),
71 config_option.name(),
72 config_file_option.name(),
73 pipeBaseOpts.instrument_option.name(),
74 ):
75 kwargs.pop(pipelineAction)
77 actions = makePipelineActions(MWCtxObj.getFrom(ctx).args)
78 mock_configs = []
79 pipeline_actions = []
80 for action in actions:
81 if action.label and action.label.endswith("-mock"): 81 ↛ 82line 81 didn't jump to line 82, because the condition on line 81 was never true
82 if action.action not in (_ACTION_CONFIG.action, _ACTION_CONFIG_FILE.action):
83 raise ValueError(f"Unexpected option for mock task config overrides: {action}")
84 mock_configs.append(action)
85 else:
86 pipeline_actions.append(action)
88 kwargs["mock_configs"] = mock_configs
89 kwargs["pipeline_actions"] = pipeline_actions
90 return kwargs
93def _unhandledShow(show: ShowInfo, cmd: str) -> None:
94 if show.unhandled: 94 ↛ 95line 94 didn't jump to line 95, because the condition on line 94 was never true
95 print(
96 f"The following '--show' options were not known to the {cmd} command: "
97 f"{', '.join(show.unhandled)}",
98 file=sys.stderr,
99 )
102@click.command(cls=PipetaskCommand, epilog=epilog, short_help="Build pipeline definition.")
103@click.pass_context
104@ctrlMpExecOpts.show_option()
105@ctrlMpExecOpts.pipeline_build_options()
106@option_section(sectionText="")
107@options_file_option()
108@catch_and_exit
109def build(ctx: click.Context, **kwargs: Any) -> None:
110 """Build and optionally save pipeline definition.
112 This does not require input data to be specified.
113 """
114 kwargs = _collectActions(ctx, **kwargs)
115 show = ShowInfo(kwargs.pop("show", []))
116 script.build(**kwargs, show=show)
117 _unhandledShow(show, "build")
120@click.command(cls=PipetaskCommand, epilog=epilog)
121@click.pass_context
122@ctrlMpExecOpts.show_option()
123@ctrlMpExecOpts.pipeline_build_options()
124@ctrlMpExecOpts.qgraph_options()
125@ctrlMpExecOpts.butler_options()
126@option_section(sectionText="")
127@options_file_option()
128@catch_and_exit
129def qgraph(ctx: click.Context, **kwargs: Any) -> None:
130 """Build and optionally save quantum graph."""
131 kwargs = _collectActions(ctx, **kwargs)
132 show = ShowInfo(kwargs.pop("show", []))
133 pipeline = script.build(**kwargs, show=show)
134 if show.handled and not show.unhandled:
135 print(
136 "No quantum graph generated. The --show option was given and all options were processed.",
137 file=sys.stderr,
138 )
139 return
140 script.qgraph(pipelineObj=pipeline, **kwargs, show=show)
141 _unhandledShow(show, "qgraph")
144@click.command(cls=PipetaskCommand, epilog=epilog)
145@ctrlMpExecOpts.run_options()
146@catch_and_exit
147def run(ctx: click.Context, **kwargs: Any) -> None:
148 """Build and execute pipeline and quantum graph."""
149 kwargs = _collectActions(ctx, **kwargs)
150 show = ShowInfo(kwargs.pop("show", []))
151 pipeline = script.build(**kwargs, show=show)
152 if show.handled and not show.unhandled:
153 print(
154 "No quantum graph generated or pipeline executed. "
155 "The --show option was given and all options were processed.",
156 file=sys.stderr,
157 )
158 return
159 qgraph = script.qgraph(pipelineObj=pipeline, **kwargs, show=show)
160 _unhandledShow(show, "run")
161 if show.handled:
162 print(
163 "No pipeline executed. The --show option was given and all options were processed.",
164 file=sys.stderr,
165 )
166 return
167 script.run(qgraphObj=qgraph, **kwargs)
170@click.command(cls=PipetaskCommand)
171@ctrlMpExecOpts.butler_config_option()
172@ctrlMpExecOpts.collection_argument()
173@confirm_option()
174@ctrlMpExecOpts.recursive_option(
175 help="""If the parent CHAINED collection has child CHAINED collections,
176 search the children until nested chains that start with the parent's name
177 are removed."""
178)
179def purge(confirm: bool, **kwargs: Any) -> None:
180 """Remove a CHAINED collection and its contained collections.
182 COLLECTION is the name of the chained collection to purge. it must not be a
183 child of any other CHAINED collections
185 Child collections must be members of exactly one collection.
187 The collections that will be removed will be printed, there will be an
188 option to continue or abort (unless using --no-confirm).
189 """
190 confirmable.confirm(partial(script.purge, **kwargs), confirm)
193@click.command(cls=PipetaskCommand)
194@ctrlMpExecOpts.butler_config_option()
195@ctrlMpExecOpts.collection_argument()
196@confirm_option()
197def cleanup(confirm: bool, **kwargs: Any) -> None:
198 """Remove non-members of CHAINED collections.
200 Removes collections that start with the same name as a CHAINED
201 collection but are not members of that collection.
202 """
203 confirmable.confirm(partial(script.cleanup, **kwargs), confirm)