Coverage for python/lsst/ctrl/mpexec/cli/cmd/commands.py: 83%

70 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-23 02:34 -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/>. 

21 

22from functools import partial 

23from typing import Any 

24 

25import click 

26import lsst.pipe.base.cli.opt as pipeBaseOpts 

27from lsst.daf.butler.cli.opt import config_file_option, config_option, confirm_option, options_file_option 

28from lsst.daf.butler.cli.utils import MWCtxObj, catch_and_exit, option_section, unwrap 

29 

30from .. import opt as ctrlMpExecOpts 

31from .. import script 

32from ..script import confirmable 

33from ..utils import _ACTION_CONFIG, _ACTION_CONFIG_FILE, PipetaskCommand, makePipelineActions 

34 

35epilog = unwrap( 

36 """Notes: 

37 

38--task, --delete, --config, --config-file, and --instrument action options can 

39appear multiple times; all values are used, in order left to right. 

40 

41FILE reads command-line options from the specified file. Data may be 

42distributed among multiple lines (e.g. one option per line). Data after # is 

43treated as a comment and ignored. Blank lines and lines starting with # are 

44ignored.) 

45""" 

46) 

47 

48 

49def _collectActions(ctx: click.Context, **kwargs: Any) -> dict[str, Any]: 

50 """Extract pipeline building options, replace them with PipelineActions, 

51 return updated `kwargs`. 

52 

53 Notes 

54 ----- 

55 The pipeline actions (task, delete, config, config_file, and instrument) 

56 must be handled in the order they appear on the command line, but the CLI 

57 specification gives them all different option names. So, instead of using 

58 the individual action options as they appear in kwargs (because 

59 invocation order can't be known), we capture the CLI arguments by 

60 overriding `click.Command.parse_args` and save them in the Context's 

61 `obj` parameter. We use `makePipelineActions` to create a list of 

62 pipeline actions from the CLI arguments and pass that list to the script 

63 function using the `pipeline_actions` kwarg name, and remove the action 

64 options from kwargs. 

65 """ 

66 for pipelineAction in ( 

67 ctrlMpExecOpts.task_option.name(), 

68 ctrlMpExecOpts.delete_option.name(), 

69 config_option.name(), 

70 config_file_option.name(), 

71 pipeBaseOpts.instrument_option.name(), 

72 ): 

73 kwargs.pop(pipelineAction) 

74 

75 actions = makePipelineActions(MWCtxObj.getFrom(ctx).args) 

76 mock_configs = [] 

77 pipeline_actions = [] 

78 for action in actions: 

79 if action.label and action.label.endswith("-mock"): 79 ↛ 80line 79 didn't jump to line 80, because the condition on line 79 was never true

80 if action.action not in (_ACTION_CONFIG.action, _ACTION_CONFIG_FILE.action): 

81 raise ValueError(f"Unexpected option for mock task config overrides: {action}") 

82 mock_configs.append(action) 

83 else: 

84 pipeline_actions.append(action) 

85 

86 kwargs["mock_configs"] = mock_configs 

87 kwargs["pipeline_actions"] = pipeline_actions 

88 return kwargs 

89 

90 

91@click.command(cls=PipetaskCommand, epilog=epilog, short_help="Build pipeline definition.") 

92@click.pass_context 

93@ctrlMpExecOpts.show_option() 

94@ctrlMpExecOpts.pipeline_build_options() 

95@option_section(sectionText="") 

96@options_file_option() 

97@catch_and_exit 

98def build(ctx: click.Context, **kwargs: Any) -> None: 

99 """Build and optionally save pipeline definition. 

100 

101 This does not require input data to be specified. 

102 """ 

103 kwargs = _collectActions(ctx, **kwargs) 

104 script.build(**kwargs) 

105 

106 

107@click.command(cls=PipetaskCommand, epilog=epilog) 

108@click.pass_context 

109@ctrlMpExecOpts.show_option() 

110@ctrlMpExecOpts.pipeline_build_options() 

111@ctrlMpExecOpts.qgraph_options() 

112@ctrlMpExecOpts.butler_options() 

113@option_section(sectionText="") 

114@options_file_option() 

115@catch_and_exit 

116def qgraph(ctx: click.Context, **kwargs: Any) -> None: 

117 """Build and optionally save quantum graph.""" 

118 kwargs = _collectActions(ctx, **kwargs) 

119 pipeline = script.build(**kwargs) 

120 script.qgraph(pipelineObj=pipeline, **kwargs) 

121 

122 

123@click.command(cls=PipetaskCommand, epilog=epilog) 

124@ctrlMpExecOpts.run_options() 

125@catch_and_exit 

126def run(ctx: click.Context, **kwargs: Any) -> None: 

127 """Build and execute pipeline and quantum graph.""" 

128 kwargs = _collectActions(ctx, **kwargs) 

129 pipeline = script.build(**kwargs) 

130 qgraph = script.qgraph(pipelineObj=pipeline, **kwargs) 

131 script.run(qgraphObj=qgraph, **kwargs) 

132 

133 

134@click.command(cls=PipetaskCommand) 

135@ctrlMpExecOpts.butler_config_option() 

136@ctrlMpExecOpts.collection_argument() 

137@confirm_option() 

138@ctrlMpExecOpts.recursive_option( 

139 help="""If the parent CHAINED collection has child CHAINED collections, 

140 search the children until nested chains that start with the parent's name 

141 are removed.""" 

142) 

143def purge(confirm: bool, **kwargs: Any) -> None: 

144 """Remove a CHAINED collection and its contained collections. 

145 

146 COLLECTION is the name of the chained collection to purge. it must not be a 

147 child of any other CHAINED collections 

148 

149 Child collections must be members of exactly one collection. 

150 

151 The collections that will be removed will be printed, there will be an 

152 option to continue or abort (unless using --no-confirm). 

153 """ 

154 confirmable.confirm(partial(script.purge, **kwargs), confirm) 

155 

156 

157@click.command(cls=PipetaskCommand) 

158@ctrlMpExecOpts.butler_config_option() 

159@ctrlMpExecOpts.collection_argument() 

160@confirm_option() 

161def cleanup(confirm: bool, **kwargs: Any) -> None: 

162 """Remove non-members of CHAINED collections. 

163 

164 Removes collections that start with the same name as a CHAINED 

165 collection but are not members of that collection. 

166 """ 

167 confirmable.confirm(partial(script.cleanup, **kwargs), confirm)