Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/absl/flags/_validators.py: 25%
56 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-05 06:32 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-05 06:32 +0000
1# Copyright 2017 The Abseil Authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""Module to enforce different constraints on flags.
17Flags validators can be registered using following functions / decorators::
19 flags.register_validator
20 @flags.validator
21 flags.register_multi_flags_validator
22 @flags.multi_flags_validator
24Three convenience functions are also provided for common flag constraints::
26 flags.mark_flag_as_required
27 flags.mark_flags_as_required
28 flags.mark_flags_as_mutual_exclusive
29 flags.mark_bool_flags_as_mutual_exclusive
31See their docstring in this module for a usage manual.
33Do NOT import this module directly. Import the flags package and use the
34aliases defined at the package level instead.
35"""
37import warnings
39from absl.flags import _exceptions
40from absl.flags import _flagvalues
41from absl.flags import _validators_classes
44def register_validator(flag_name,
45 checker,
46 message='Flag validation failed',
47 flag_values=_flagvalues.FLAGS):
48 """Adds a constraint, which will be enforced during program execution.
50 The constraint is validated when flags are initially parsed, and after each
51 change of the corresponding flag's value.
53 Args:
54 flag_name: str | FlagHolder, name or holder of the flag to be checked.
55 Positional-only parameter.
56 checker: callable, a function to validate the flag.
58 * input - A single positional argument: The value of the corresponding
59 flag (string, boolean, etc. This value will be passed to checker
60 by the library).
61 * output - bool, True if validator constraint is satisfied.
62 If constraint is not satisfied, it should either ``return False`` or
63 ``raise flags.ValidationError(desired_error_message)``.
65 message: str, error text to be shown to the user if checker returns False.
66 If checker raises flags.ValidationError, message from the raised
67 error will be shown.
68 flag_values: flags.FlagValues, optional FlagValues instance to validate
69 against.
71 Raises:
72 AttributeError: Raised when flag_name is not registered as a valid flag
73 name.
74 ValueError: Raised when flag_values is non-default and does not match the
75 FlagValues of the provided FlagHolder instance.
76 """
77 flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
78 v = _validators_classes.SingleFlagValidator(flag_name, checker, message)
79 _add_validator(flag_values, v)
82def validator(flag_name, message='Flag validation failed',
83 flag_values=_flagvalues.FLAGS):
84 """A function decorator for defining a flag validator.
86 Registers the decorated function as a validator for flag_name, e.g.::
88 @flags.validator('foo')
89 def _CheckFoo(foo):
90 ...
92 See :func:`register_validator` for the specification of checker function.
94 Args:
95 flag_name: str | FlagHolder, name or holder of the flag to be checked.
96 Positional-only parameter.
97 message: str, error text to be shown to the user if checker returns False.
98 If checker raises flags.ValidationError, message from the raised
99 error will be shown.
100 flag_values: flags.FlagValues, optional FlagValues instance to validate
101 against.
102 Returns:
103 A function decorator that registers its function argument as a validator.
104 Raises:
105 AttributeError: Raised when flag_name is not registered as a valid flag
106 name.
107 """
109 def decorate(function):
110 register_validator(flag_name, function,
111 message=message,
112 flag_values=flag_values)
113 return function
114 return decorate
117def register_multi_flags_validator(flag_names,
118 multi_flags_checker,
119 message='Flags validation failed',
120 flag_values=_flagvalues.FLAGS):
121 """Adds a constraint to multiple flags.
123 The constraint is validated when flags are initially parsed, and after each
124 change of the corresponding flag's value.
126 Args:
127 flag_names: [str | FlagHolder], a list of the flag names or holders to be
128 checked. Positional-only parameter.
129 multi_flags_checker: callable, a function to validate the flag.
131 * input - dict, with keys() being flag_names, and value for each key
132 being the value of the corresponding flag (string, boolean, etc).
133 * output - bool, True if validator constraint is satisfied.
134 If constraint is not satisfied, it should either return False or
135 raise flags.ValidationError.
137 message: str, error text to be shown to the user if checker returns False.
138 If checker raises flags.ValidationError, message from the raised
139 error will be shown.
140 flag_values: flags.FlagValues, optional FlagValues instance to validate
141 against.
143 Raises:
144 AttributeError: Raised when a flag is not registered as a valid flag name.
145 ValueError: Raised when multiple FlagValues are used in the same
146 invocation. This can occur when FlagHolders have different `_flagvalues`
147 or when str-type flag_names entries are present and the `flag_values`
148 argument does not match that of provided FlagHolder(s).
149 """
150 flag_names, flag_values = _flagvalues.resolve_flag_refs(
151 flag_names, flag_values)
152 v = _validators_classes.MultiFlagsValidator(
153 flag_names, multi_flags_checker, message)
154 _add_validator(flag_values, v)
157def multi_flags_validator(flag_names,
158 message='Flag validation failed',
159 flag_values=_flagvalues.FLAGS):
160 """A function decorator for defining a multi-flag validator.
162 Registers the decorated function as a validator for flag_names, e.g.::
164 @flags.multi_flags_validator(['foo', 'bar'])
165 def _CheckFooBar(flags_dict):
166 ...
168 See :func:`register_multi_flags_validator` for the specification of checker
169 function.
171 Args:
172 flag_names: [str | FlagHolder], a list of the flag names or holders to be
173 checked. Positional-only parameter.
174 message: str, error text to be shown to the user if checker returns False.
175 If checker raises flags.ValidationError, message from the raised
176 error will be shown.
177 flag_values: flags.FlagValues, optional FlagValues instance to validate
178 against.
180 Returns:
181 A function decorator that registers its function argument as a validator.
183 Raises:
184 AttributeError: Raised when a flag is not registered as a valid flag name.
185 """
187 def decorate(function):
188 register_multi_flags_validator(flag_names,
189 function,
190 message=message,
191 flag_values=flag_values)
192 return function
194 return decorate
197def mark_flag_as_required(flag_name, flag_values=_flagvalues.FLAGS):
198 """Ensures that flag is not None during program execution.
200 Registers a flag validator, which will follow usual validator rules.
201 Important note: validator will pass for any non-``None`` value, such as
202 ``False``, ``0`` (zero), ``''`` (empty string) and so on.
204 If your module might be imported by others, and you only wish to make the flag
205 required when the module is directly executed, call this method like this::
207 if __name__ == '__main__':
208 flags.mark_flag_as_required('your_flag_name')
209 app.run()
211 Args:
212 flag_name: str | FlagHolder, name or holder of the flag.
213 Positional-only parameter.
214 flag_values: flags.FlagValues, optional :class:`~absl.flags.FlagValues`
215 instance where the flag is defined.
216 Raises:
217 AttributeError: Raised when flag_name is not registered as a valid flag
218 name.
219 ValueError: Raised when flag_values is non-default and does not match the
220 FlagValues of the provided FlagHolder instance.
221 """
222 flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values)
223 if flag_values[flag_name].default is not None:
224 warnings.warn(
225 'Flag --%s has a non-None default value; therefore, '
226 'mark_flag_as_required will pass even if flag is not specified in the '
227 'command line!' % flag_name,
228 stacklevel=2)
229 register_validator(
230 flag_name,
231 lambda value: value is not None,
232 message='Flag --{} must have a value other than None.'.format(flag_name),
233 flag_values=flag_values)
236def mark_flags_as_required(flag_names, flag_values=_flagvalues.FLAGS):
237 """Ensures that flags are not None during program execution.
239 If your module might be imported by others, and you only wish to make the flag
240 required when the module is directly executed, call this method like this::
242 if __name__ == '__main__':
243 flags.mark_flags_as_required(['flag1', 'flag2', 'flag3'])
244 app.run()
246 Args:
247 flag_names: Sequence[str | FlagHolder], names or holders of the flags.
248 flag_values: flags.FlagValues, optional FlagValues instance where the flags
249 are defined.
250 Raises:
251 AttributeError: If any of flag name has not already been defined as a flag.
252 """
253 for flag_name in flag_names:
254 mark_flag_as_required(flag_name, flag_values)
257def mark_flags_as_mutual_exclusive(flag_names, required=False,
258 flag_values=_flagvalues.FLAGS):
259 """Ensures that only one flag among flag_names is not None.
261 Important note: This validator checks if flag values are ``None``, and it does
262 not distinguish between default and explicit values. Therefore, this validator
263 does not make sense when applied to flags with default values other than None,
264 including other false values (e.g. ``False``, ``0``, ``''``, ``[]``). That
265 includes multi flags with a default value of ``[]`` instead of None.
267 Args:
268 flag_names: [str | FlagHolder], names or holders of flags.
269 Positional-only parameter.
270 required: bool. If true, exactly one of the flags must have a value other
271 than None. Otherwise, at most one of the flags can have a value other
272 than None, and it is valid for all of the flags to be None.
273 flag_values: flags.FlagValues, optional FlagValues instance where the flags
274 are defined.
276 Raises:
277 ValueError: Raised when multiple FlagValues are used in the same
278 invocation. This can occur when FlagHolders have different `_flagvalues`
279 or when str-type flag_names entries are present and the `flag_values`
280 argument does not match that of provided FlagHolder(s).
281 """
282 flag_names, flag_values = _flagvalues.resolve_flag_refs(
283 flag_names, flag_values)
284 for flag_name in flag_names:
285 if flag_values[flag_name].default is not None:
286 warnings.warn(
287 'Flag --{} has a non-None default value. That does not make sense '
288 'with mark_flags_as_mutual_exclusive, which checks whether the '
289 'listed flags have a value other than None.'.format(flag_name),
290 stacklevel=2)
292 def validate_mutual_exclusion(flags_dict):
293 flag_count = sum(1 for val in flags_dict.values() if val is not None)
294 if flag_count == 1 or (not required and flag_count == 0):
295 return True
296 raise _exceptions.ValidationError(
297 '{} one of ({}) must have a value other than None.'.format(
298 'Exactly' if required else 'At most', ', '.join(flag_names)))
300 register_multi_flags_validator(
301 flag_names, validate_mutual_exclusion, flag_values=flag_values)
304def mark_bool_flags_as_mutual_exclusive(flag_names, required=False,
305 flag_values=_flagvalues.FLAGS):
306 """Ensures that only one flag among flag_names is True.
308 Args:
309 flag_names: [str | FlagHolder], names or holders of flags.
310 Positional-only parameter.
311 required: bool. If true, exactly one flag must be True. Otherwise, at most
312 one flag can be True, and it is valid for all flags to be False.
313 flag_values: flags.FlagValues, optional FlagValues instance where the flags
314 are defined.
316 Raises:
317 ValueError: Raised when multiple FlagValues are used in the same
318 invocation. This can occur when FlagHolders have different `_flagvalues`
319 or when str-type flag_names entries are present and the `flag_values`
320 argument does not match that of provided FlagHolder(s).
321 """
322 flag_names, flag_values = _flagvalues.resolve_flag_refs(
323 flag_names, flag_values)
324 for flag_name in flag_names:
325 if not flag_values[flag_name].boolean:
326 raise _exceptions.ValidationError(
327 'Flag --{} is not Boolean, which is required for flags used in '
328 'mark_bool_flags_as_mutual_exclusive.'.format(flag_name))
330 def validate_boolean_mutual_exclusion(flags_dict):
331 flag_count = sum(bool(val) for val in flags_dict.values())
332 if flag_count == 1 or (not required and flag_count == 0):
333 return True
334 raise _exceptions.ValidationError(
335 '{} one of ({}) must be True.'.format(
336 'Exactly' if required else 'At most', ', '.join(flag_names)))
338 register_multi_flags_validator(
339 flag_names, validate_boolean_mutual_exclusion, flag_values=flag_values)
342def _add_validator(fv, validator_instance):
343 """Register new flags validator to be checked.
345 Args:
346 fv: flags.FlagValues, the FlagValues instance to add the validator.
347 validator_instance: validators.Validator, the validator to add.
348 Raises:
349 KeyError: Raised when validators work with a non-existing flag.
350 """
351 for flag_name in validator_instance.get_flags_names():
352 fv[flag_name].validators.append(validator_instance)