Coverage for python/lsst/daf/butler/cli/utils.py : 27%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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 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 click
23import os
24from unittest.mock import MagicMock
26from ..core.utils import iterable
29# CLI_BUTLER_MOCK_ENV is set by some tests as an environment variable, it
30# indicates to the cli_handle_exception function that instead of executing the
31# command implementation function it should use the Mocker class for unit test
32# verification.
33mockEnvVarKey = "CLI_BUTLER_MOCK_ENV"
34mockEnvVar = {mockEnvVarKey: "1"}
37class Mocker:
39 mock = MagicMock()
41 def __init__(self, *args, **kwargs):
42 """Mocker is a helper class for unit tests. It can be imported and
43 called and later imported again and call can be verified.
45 For convenience, constructor arguments are forwarded to the call
46 function.
47 """
48 self.__call__(*args, **kwargs)
50 def __call__(self, *args, **kwargs):
51 """Creates a MagicMock and stores it in a static variable that can
52 later be verified.
53 """
54 Mocker.mock(*args, **kwargs)
57def split_commas(context, param, values):
58 """Process a tuple of values, where each value may contain comma-separated
59 values, and return a single list of all the passed-in values.
61 This function can be passed to the 'callback' argument of a click.option to
62 allow it to process comma-separated values (e.g. "--my-opt a,b,c").
64 Parameters
65 ----------
66 context : `click.Context` or `None`
67 The current execution context. Unused, but Click always passes it to
68 callbacks.
69 param : `click.core.Option` or `None`
70 The parameter being handled. Unused, but Click always passes it to
71 callbacks.
72 values : [`str`]
73 All the values passed for this option. Strings may contain commas,
74 which will be treated as delimiters for separate values.
76 Returns
77 -------
78 list of string
79 The passed in values separated by commas and combined into a single
80 list.
81 """
82 valueList = []
83 for value in iterable(values):
84 valueList.extend(value.split(","))
85 return valueList
88def split_kv(context, param, values, separator="="):
89 """Process a tuple of values that are key-value pairs separated by a given
90 separator. Multiple pairs may be comma separated. Return a dictionary of
91 all the passed-in values.
93 This function can be passed to the 'callback' argument of a click.option to
94 allow it to process comma-separated values (e.g. "--my-opt a=1,b=2").
96 Parameters
97 ----------
98 context : `click.Context` or `None`
99 The current execution context. Unused, but Click always passes it to
100 callbacks.
101 param : `click.core.Option` or `None`
102 The parameter being handled. Unused, but Click always passes it to
103 callbacks.
104 values : [`str`]
105 All the values passed for this option. Strings may contain commas,
106 which will be treated as delimiters for separate values.
107 separator : str, optional
108 The character that separates key-value pairs. May not be a comma or an
109 empty space (for space separators use Click's default implementation
110 for tuples; `type=(str, str)`). By default "=".
112 Returns
113 -------
114 `dict` : [`str`, `str`]
115 The passed-in values in dict form.
117 Raises
118 ------
119 `click.ClickException`
120 Raised if the separator is not found in an entry, or if duplicate keys
121 are encountered.
122 """
123 if "," == separator or " " == separator:
124 raise RuntimeError(f"'{separator}' is not a supported separator for key-value pairs.")
125 vals = split_commas(context, param, values)
126 ret = {}
127 for val in vals:
128 try:
129 k, v = val.split(separator)
130 except ValueError:
131 raise click.ClickException(f"Missing or invalid key-value separator in value '{val}'")
132 if k in ret:
133 raise click.ClickException(f"Duplicate entries for '{k}' in '{values}'")
134 ret[k] = v
135 return ret
138def to_upper(context, param, value):
139 """Convert a value to upper case.
141 Parameters
142 ----------
143 context : click.Context
145 values : string
146 The value to be converted.
148 Returns
149 -------
150 string
151 A copy of the passed-in value, converted to upper case.
152 """
153 return value.upper()
156def cli_handle_exception(func, *args, **kwargs):
157 """Wrap a function call in an exception handler that raises a
158 ClickException if there is an Exception.
160 Also provides support for unit testing by testing for an environment
161 variable, and if it is present prints the function name, args, and kwargs
162 to stdout so they can be read and verified by the unit test code.
164 Parameters
165 ----------
166 func : function
167 A function to be called and exceptions handled. Will pass args & kwargs
168 to the function.
170 Returns
171 -------
172 The result of calling func.
174 Raises
175 ------
176 click.ClickException
177 An exception to be handled by the Click CLI tool.
178 """
179 if mockEnvVarKey in os.environ:
180 Mocker(*args, **kwargs)
181 return
182 try:
183 return func(*args, **kwargs)
184 except Exception as err:
185 raise click.ClickException(err)