Coverage for python/lsst/ctrl/bps/cli/cmd/commands.py: 87%

108 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-09-29 09:34 +0000

1# This file is part of ctrl_bps. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <https://www.gnu.org/licenses/>. 

27"""Subcommand definitions.""" 

28 

29from collections.abc import Iterator 

30from contextlib import contextmanager 

31 

32import click 

33 

34from lsst.ctrl.mpexec.cli.opt import save_qgraph_option 

35from lsst.daf.butler.cli.utils import MWCommand 

36 

37from ... import BpsSubprocessError 

38from ...drivers import ( 

39 acquire_qgraph_driver, 

40 batch_acquire_driver, 

41 batch_prepare_driver, 

42 cancel_driver, 

43 cluster_qgraph_driver, 

44 ping_driver, 

45 prepare_driver, 

46 report_driver, 

47 restart_driver, 

48 status_driver, 

49 submit_driver, 

50 submitcmd_driver, 

51 transform_driver, 

52) 

53from .. import opt 

54 

55 

56@contextmanager 

57def catch_errors() -> Iterator[None]: 

58 """Handle errors that occurred during command execution. 

59 

60 Returns 

61 ------- 

62 context : `contextlib.AbstractContextManager` [ `None` ] 

63 A context manager that does not return a value when entered. 

64 

65 Notes 

66 ----- 

67 At the moment, the main responsibility of this context manager is to catch 

68 the exception related to failures of the commands BPS runs in its 

69 subprocesses to force Click CLI report command's actual exit code instead 

70 of always returning 1. 

71 """ 

72 try: 

73 yield None 

74 except BpsSubprocessError as e: 

75 click.echo(e) 

76 click.get_current_context().exit(e.errno) 

77 except BaseException: 

78 raise 

79 

80 

81class BpsCommand(MWCommand): 

82 """Command subclass with bps-command specific overrides.""" 

83 

84 extra_epilog = "See 'bps --help' for more options." 

85 

86 

87@click.command(cls=BpsCommand) 

88@opt.config_file_argument(required=True) 

89@opt.submission_options() 

90def acquire(*args, **kwargs): 

91 """Create a new quantum graph or read existing one from a file.""" 

92 with catch_errors(): 

93 acquire_qgraph_driver(*args, **kwargs) 

94 

95 

96@click.command(cls=BpsCommand) 

97@opt.config_file_argument(required=True) 

98@opt.submission_options() 

99def cluster(*args, **kwargs): 

100 """Create a clustered quantum graph.""" 

101 with catch_errors(): 

102 cluster_qgraph_driver(*args, **kwargs) 

103 

104 

105@click.command(cls=BpsCommand) 

106@opt.config_file_argument(required=True) 

107@opt.submission_options() 

108def transform(*args, **kwargs): 

109 """Transform a quantum graph to a generic workflow.""" 

110 with catch_errors(): 

111 transform_driver(*args, **kwargs) 

112 

113 

114@click.command(cls=BpsCommand) 

115@opt.config_file_argument(required=True) 

116@opt.wms_service_option() 

117@opt.submission_options() 

118def prepare(*args, **kwargs): 

119 """Prepare a workflow for submission.""" 

120 with catch_errors(): 

121 prepare_driver(*args, **kwargs) 

122 

123 

124@click.command(cls=BpsCommand) 

125@opt.config_file_argument(required=True) 

126@opt.wms_service_option() 

127@opt.compute_site_option() 

128@opt.submission_options() 

129def submit(*args, **kwargs): 

130 """Submit a workflow for execution.""" 

131 with catch_errors(): 

132 submit_driver(*args, **kwargs) 

133 

134 

135@click.command(cls=BpsCommand) 

136@opt.wms_service_option() 

137@click.option("--id", "run_id", help="Run id of workflow to restart.") 

138def restart(*args, **kwargs): 

139 """Restart a failed workflow.""" 

140 restart_driver(*args, **kwargs) 

141 

142 

143@click.command(cls=BpsCommand) 

144@opt.wms_service_option() 

145@click.option("--id", "run_id", help="Restrict report to specific WMS run id.") 

146@click.option("--user", help="Restrict report to specific user.") 

147@click.option("--hist", "hist_days", default=0.0, help="Search WMS history X days for completed info.") 

148@click.option("--pass-thru", help="Pass the given string to the WMS service class.") 

149@click.option( 

150 "--return-exit-codes", 

151 is_flag=True, 

152 show_default=True, 

153 default=False, 

154 help="Return exit codes from jobs with a non-success status.", 

155) 

156@click.option( 

157 "--global/--no-global", 

158 "is_global", 

159 default=False, 

160 help="Query all available job queues for job information.", 

161) 

162def report(*args, **kwargs): 

163 """Display execution information for submitted workflows.""" 

164 report_driver(*args, **kwargs) 

165 

166 

167@click.command(cls=BpsCommand) 

168@opt.wms_service_option() 

169@click.option("--id", "run_id", required=True, help="Restrict report to specific WMS run id.") 

170@click.option("--hist", "hist_days", default=0.0, help="Search WMS history X days for completed info.") 

171@click.option( 

172 "--global/--no-global", 

173 "is_global", 

174 default=False, 

175 help="Query all available job queues for job information.", 

176) 

177def status(*args, **kwargs): 

178 """Exit with execution status of single submitted workflow.""" 

179 # Note: Using return statement doesn't actually return the value 

180 # to the shell. Using click function instead. 

181 click.get_current_context().exit(status_driver(*args, **kwargs)) 

182 

183 

184@click.command(cls=BpsCommand) 

185@opt.wms_service_option() 

186@click.option("--id", "run_id", help="Run id of workflow to cancel.") 

187@click.option("--user", help="User for which to cancel all submitted workflows.") 

188@click.option( 

189 "--require-bps/--skip-require-bps", 

190 "require_bps", 

191 default=True, 

192 show_default=True, 

193 help="Only cancel jobs submitted via bps.", 

194) 

195@click.option("--pass-thru", "pass_thru", default="", help="Pass the given string to the WMS service.") 

196@click.option( 

197 "--global/--no-global", 

198 "is_global", 

199 default=False, 

200 help="Cancel jobs matching the search criteria from all job queues.", 

201) 

202def cancel(*args, **kwargs): 

203 """Cancel submitted workflow(s).""" 

204 cancel_driver(*args, **kwargs) 

205 

206 

207@click.command(cls=BpsCommand) 

208@opt.wms_service_option() 

209@click.option("--pass-thru", "pass_thru", default="", help="Pass the given string to the WMS service.") 

210def ping(*args, **kwargs): 

211 """Ping workflow services.""" 

212 # Note: Using return statement doesn't actually return the value 

213 # to the shell. Using click function instead. 

214 click.get_current_context().exit(ping_driver(*args, **kwargs)) 

215 

216 

217@click.command(cls=BpsCommand) 

218@opt.config_file_argument(required=True) 

219@opt.wms_service_option() 

220@opt.compute_site_option() 

221@click.option("--dry-run", "dry_run", is_flag=True, help="Prepare workflow but don't submit") 

222def submitcmd(*args, **kwargs): 

223 """Submit a command for execution.""" 

224 submitcmd_driver(*args, **kwargs) 

225 

226 

227@click.command(cls=BpsCommand) 

228@opt.config_file_argument(required=True) 

229@opt.submission_options() 

230@save_qgraph_option() 

231def batch_acquire(*args, **kwargs): 

232 """Run inside a batch job to create a new quantum graph.""" 

233 with catch_errors(): 

234 batch_acquire_driver(*args, **kwargs) 

235 

236 

237@click.command(cls=BpsCommand) 

238@opt.config_file_argument(required=True) 

239@opt.submission_options() 

240def batch_prepare(*args, **kwargs): 

241 """Run payload workflow preparation inside a batch job.""" 

242 with catch_errors(): 

243 batch_prepare_driver(*args, **kwargs)