Coverage for python/lsst/daf/butler/cli/cmd/_remove_runs.py: 48%
61 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-15 02:03 -0700
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-15 02:03 -0700
1# This file is part of daf_butler.
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 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 <http://www.gnu.org/licenses/>.
27from __future__ import annotations
29__all__ = ["remove_runs"]
31from collections.abc import Mapping, Sequence
32from typing import Any
34import click
36from ... import script
37from ..opt import collection_argument, confirm_option, options_file_option, repo_argument
38from ..utils import ButlerCommand
39from .commands import existingRepoHelp
41# messages emitted by remove-runs, defined separately for use in unit
42# tests.
43noRunCollectionsMsg = "No RUN collections were found."
44willRemoveRunsMsg = "The following RUN collections will be removed:"
45willRemoveDatasetsMsg = "The following datasets will be removed:"
46didRemoveRunsMsg = "The following RUN collections were removed:"
47didRemoveDatasetsMsg = "The following datasets were removed:"
48removedRunsMsg = "Removed collections"
49abortedMsg = "Aborted."
50requiresConfirmationMsg = (
51 "Removing runs that are in parent CHAINED collections requires confirmation. "
52 "\nTry again without --no-confirm to confirm removal of RUN collections from parents, "
53 "or add the --force flag to skip confirmation."
54)
55willUnlinkMsg = "{run}: will be unlinked from {parents}"
56didUnlinkMsg = "{run}: was removed and unlinked from {parents}"
57mustBeUnlinkedMsg = "{run}: must be unlinked from {parents}"
60def _quoted(items: Sequence[str]) -> list[str]:
61 return [f'"{i}"' for i in items]
64def _print_remove(will: bool, runs: Sequence[script.RemoveRun], datasets: Mapping[str, int]) -> None:
65 """Print the formatted remove statement.
67 Parameters
68 ----------
69 will : `bool`
70 True if remove "will" happen, False if the remove "did" happen.
71 runs : `~collections.abc.Sequence` [`str`]
72 The RUNs that will be or were removed.
73 datasets : `~collections.abc.Mapping` [`str`, `int`]
74 The dataset types & count that will be or were removed.
75 """
76 print(willRemoveRunsMsg if will else didRemoveRunsMsg)
77 unlinkMsg = willUnlinkMsg if will else didUnlinkMsg
78 for run in runs:
79 if run.parents:
80 print(unlinkMsg.format(run=run.name, parents=", ".join(_quoted(run.parents))))
81 else:
82 print(run.name)
83 print("\n" + willRemoveDatasetsMsg if will else didRemoveDatasetsMsg)
84 print(", ".join([f"{i[0]}({i[1]})" for i in datasets.items()]))
87def _print_requires_confirmation(runs: Sequence[script.RemoveRun], datasets: Mapping[str, int]) -> None:
88 print(requiresConfirmationMsg)
89 for run in runs:
90 if run.parents:
91 print(mustBeUnlinkedMsg.format(run=run.name, parents=", ".join(_quoted(run.parents))))
94@click.command(cls=ButlerCommand)
95@click.pass_context
96@repo_argument(
97 help=existingRepoHelp,
98 required=True,
99)
100@collection_argument(
101 help="COLLECTION is a glob-style expression that identifies the RUN collection(s) to remove."
102)
103@confirm_option()
104@click.option(
105 "--force",
106 is_flag=True,
107 help="Required to remove RUN collections from parent collections if using --no-confirm.",
108)
109@options_file_option()
110def remove_runs(context: click.Context, confirm: bool, force: bool, **kwargs: Any) -> None:
111 """Remove one or more RUN collections.
113 This command can be used to remove RUN collections and the datasets within
114 them.
116 Parameters
117 ----------
118 context : `click.Context`
119 Context provided by Click.
120 confirm : `bool`
121 Confirmation for removal of the run.
122 force : `bool`
123 Force removal.
124 **kwargs : `dict` [`str`, `str`]
125 The parameters to pass to `~lsst.daf.butler.script.removeRuns`.
126 """
127 result = script.removeRuns(**kwargs)
128 canRemoveRuns = len(result.runs)
129 if not canRemoveRuns:
130 print(noRunCollectionsMsg)
131 return
132 if confirm:
133 _print_remove(True, result.runs, result.datasets)
134 doContinue = click.confirm(text="Continue?", default=False)
135 if doContinue:
136 result.onConfirmation()
137 print(removedRunsMsg)
138 else:
139 print(abortedMsg)
140 else:
141 # if the user opted out of confirmation but there are runs with
142 # parent collections then they must confirm; print a message
143 # and exit.
144 if any(run.parents for run in result.runs) and not force:
145 _print_requires_confirmation(result.runs, result.datasets)
146 context.exit(1)
147 result.onConfirmation()
148 _print_remove(False, result.runs, result.datasets)