Spaces:
Running
Running
# Copyright 2017 The Abseil Authors. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
"""Module to enforce different constraints on flags. | |
Flags validators can be registered using following functions / decorators:: | |
flags.register_validator | |
@flags.validator | |
flags.register_multi_flags_validator | |
@flags.multi_flags_validator | |
Three convenience functions are also provided for common flag constraints:: | |
flags.mark_flag_as_required | |
flags.mark_flags_as_required | |
flags.mark_flags_as_mutual_exclusive | |
flags.mark_bool_flags_as_mutual_exclusive | |
See their docstring in this module for a usage manual. | |
Do NOT import this module directly. Import the flags package and use the | |
aliases defined at the package level instead. | |
""" | |
import warnings | |
from absl.flags import _exceptions | |
from absl.flags import _flagvalues | |
from absl.flags import _validators_classes | |
def register_validator(flag_name, | |
checker, | |
message='Flag validation failed', | |
flag_values=_flagvalues.FLAGS): | |
"""Adds a constraint, which will be enforced during program execution. | |
The constraint is validated when flags are initially parsed, and after each | |
change of the corresponding flag's value. | |
Args: | |
flag_name: str | FlagHolder, name or holder of the flag to be checked. | |
Positional-only parameter. | |
checker: callable, a function to validate the flag. | |
* input - A single positional argument: The value of the corresponding | |
flag (string, boolean, etc. This value will be passed to checker | |
by the library). | |
* output - bool, True if validator constraint is satisfied. | |
If constraint is not satisfied, it should either ``return False`` or | |
``raise flags.ValidationError(desired_error_message)``. | |
message: str, error text to be shown to the user if checker returns False. | |
If checker raises flags.ValidationError, message from the raised | |
error will be shown. | |
flag_values: flags.FlagValues, optional FlagValues instance to validate | |
against. | |
Raises: | |
AttributeError: Raised when flag_name is not registered as a valid flag | |
name. | |
ValueError: Raised when flag_values is non-default and does not match the | |
FlagValues of the provided FlagHolder instance. | |
""" | |
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values) | |
v = _validators_classes.SingleFlagValidator(flag_name, checker, message) | |
_add_validator(flag_values, v) | |
def validator(flag_name, message='Flag validation failed', | |
flag_values=_flagvalues.FLAGS): | |
"""A function decorator for defining a flag validator. | |
Registers the decorated function as a validator for flag_name, e.g.:: | |
@flags.validator('foo') | |
def _CheckFoo(foo): | |
... | |
See :func:`register_validator` for the specification of checker function. | |
Args: | |
flag_name: str | FlagHolder, name or holder of the flag to be checked. | |
Positional-only parameter. | |
message: str, error text to be shown to the user if checker returns False. | |
If checker raises flags.ValidationError, message from the raised | |
error will be shown. | |
flag_values: flags.FlagValues, optional FlagValues instance to validate | |
against. | |
Returns: | |
A function decorator that registers its function argument as a validator. | |
Raises: | |
AttributeError: Raised when flag_name is not registered as a valid flag | |
name. | |
""" | |
def decorate(function): | |
register_validator(flag_name, function, | |
message=message, | |
flag_values=flag_values) | |
return function | |
return decorate | |
def register_multi_flags_validator(flag_names, | |
multi_flags_checker, | |
message='Flags validation failed', | |
flag_values=_flagvalues.FLAGS): | |
"""Adds a constraint to multiple flags. | |
The constraint is validated when flags are initially parsed, and after each | |
change of the corresponding flag's value. | |
Args: | |
flag_names: [str | FlagHolder], a list of the flag names or holders to be | |
checked. Positional-only parameter. | |
multi_flags_checker: callable, a function to validate the flag. | |
* input - dict, with keys() being flag_names, and value for each key | |
being the value of the corresponding flag (string, boolean, etc). | |
* output - bool, True if validator constraint is satisfied. | |
If constraint is not satisfied, it should either return False or | |
raise flags.ValidationError. | |
message: str, error text to be shown to the user if checker returns False. | |
If checker raises flags.ValidationError, message from the raised | |
error will be shown. | |
flag_values: flags.FlagValues, optional FlagValues instance to validate | |
against. | |
Raises: | |
AttributeError: Raised when a flag is not registered as a valid flag name. | |
ValueError: Raised when multiple FlagValues are used in the same | |
invocation. This can occur when FlagHolders have different `_flagvalues` | |
or when str-type flag_names entries are present and the `flag_values` | |
argument does not match that of provided FlagHolder(s). | |
""" | |
flag_names, flag_values = _flagvalues.resolve_flag_refs( | |
flag_names, flag_values) | |
v = _validators_classes.MultiFlagsValidator( | |
flag_names, multi_flags_checker, message) | |
_add_validator(flag_values, v) | |
def multi_flags_validator(flag_names, | |
message='Flag validation failed', | |
flag_values=_flagvalues.FLAGS): | |
"""A function decorator for defining a multi-flag validator. | |
Registers the decorated function as a validator for flag_names, e.g.:: | |
@flags.multi_flags_validator(['foo', 'bar']) | |
def _CheckFooBar(flags_dict): | |
... | |
See :func:`register_multi_flags_validator` for the specification of checker | |
function. | |
Args: | |
flag_names: [str | FlagHolder], a list of the flag names or holders to be | |
checked. Positional-only parameter. | |
message: str, error text to be shown to the user if checker returns False. | |
If checker raises flags.ValidationError, message from the raised | |
error will be shown. | |
flag_values: flags.FlagValues, optional FlagValues instance to validate | |
against. | |
Returns: | |
A function decorator that registers its function argument as a validator. | |
Raises: | |
AttributeError: Raised when a flag is not registered as a valid flag name. | |
""" | |
def decorate(function): | |
register_multi_flags_validator(flag_names, | |
function, | |
message=message, | |
flag_values=flag_values) | |
return function | |
return decorate | |
def mark_flag_as_required(flag_name, flag_values=_flagvalues.FLAGS): | |
"""Ensures that flag is not None during program execution. | |
Registers a flag validator, which will follow usual validator rules. | |
Important note: validator will pass for any non-``None`` value, such as | |
``False``, ``0`` (zero), ``''`` (empty string) and so on. | |
If your module might be imported by others, and you only wish to make the flag | |
required when the module is directly executed, call this method like this:: | |
if __name__ == '__main__': | |
flags.mark_flag_as_required('your_flag_name') | |
app.run() | |
Args: | |
flag_name: str | FlagHolder, name or holder of the flag. | |
Positional-only parameter. | |
flag_values: flags.FlagValues, optional :class:`~absl.flags.FlagValues` | |
instance where the flag is defined. | |
Raises: | |
AttributeError: Raised when flag_name is not registered as a valid flag | |
name. | |
ValueError: Raised when flag_values is non-default and does not match the | |
FlagValues of the provided FlagHolder instance. | |
""" | |
flag_name, flag_values = _flagvalues.resolve_flag_ref(flag_name, flag_values) | |
if flag_values[flag_name].default is not None: | |
warnings.warn( | |
'Flag --%s has a non-None default value; therefore, ' | |
'mark_flag_as_required will pass even if flag is not specified in the ' | |
'command line!' % flag_name, | |
stacklevel=2) | |
register_validator( | |
flag_name, | |
lambda value: value is not None, | |
message='Flag --{} must have a value other than None.'.format(flag_name), | |
flag_values=flag_values) | |
def mark_flags_as_required(flag_names, flag_values=_flagvalues.FLAGS): | |
"""Ensures that flags are not None during program execution. | |
If your module might be imported by others, and you only wish to make the flag | |
required when the module is directly executed, call this method like this:: | |
if __name__ == '__main__': | |
flags.mark_flags_as_required(['flag1', 'flag2', 'flag3']) | |
app.run() | |
Args: | |
flag_names: Sequence[str | FlagHolder], names or holders of the flags. | |
flag_values: flags.FlagValues, optional FlagValues instance where the flags | |
are defined. | |
Raises: | |
AttributeError: If any of flag name has not already been defined as a flag. | |
""" | |
for flag_name in flag_names: | |
mark_flag_as_required(flag_name, flag_values) | |
def mark_flags_as_mutual_exclusive(flag_names, required=False, | |
flag_values=_flagvalues.FLAGS): | |
"""Ensures that only one flag among flag_names is not None. | |
Important note: This validator checks if flag values are ``None``, and it does | |
not distinguish between default and explicit values. Therefore, this validator | |
does not make sense when applied to flags with default values other than None, | |
including other false values (e.g. ``False``, ``0``, ``''``, ``[]``). That | |
includes multi flags with a default value of ``[]`` instead of None. | |
Args: | |
flag_names: [str | FlagHolder], names or holders of flags. | |
Positional-only parameter. | |
required: bool. If true, exactly one of the flags must have a value other | |
than None. Otherwise, at most one of the flags can have a value other | |
than None, and it is valid for all of the flags to be None. | |
flag_values: flags.FlagValues, optional FlagValues instance where the flags | |
are defined. | |
Raises: | |
ValueError: Raised when multiple FlagValues are used in the same | |
invocation. This can occur when FlagHolders have different `_flagvalues` | |
or when str-type flag_names entries are present and the `flag_values` | |
argument does not match that of provided FlagHolder(s). | |
""" | |
flag_names, flag_values = _flagvalues.resolve_flag_refs( | |
flag_names, flag_values) | |
for flag_name in flag_names: | |
if flag_values[flag_name].default is not None: | |
warnings.warn( | |
'Flag --{} has a non-None default value. That does not make sense ' | |
'with mark_flags_as_mutual_exclusive, which checks whether the ' | |
'listed flags have a value other than None.'.format(flag_name), | |
stacklevel=2) | |
def validate_mutual_exclusion(flags_dict): | |
flag_count = sum(1 for val in flags_dict.values() if val is not None) | |
if flag_count == 1 or (not required and flag_count == 0): | |
return True | |
raise _exceptions.ValidationError( | |
'{} one of ({}) must have a value other than None.'.format( | |
'Exactly' if required else 'At most', ', '.join(flag_names))) | |
register_multi_flags_validator( | |
flag_names, validate_mutual_exclusion, flag_values=flag_values) | |
def mark_bool_flags_as_mutual_exclusive(flag_names, required=False, | |
flag_values=_flagvalues.FLAGS): | |
"""Ensures that only one flag among flag_names is True. | |
Args: | |
flag_names: [str | FlagHolder], names or holders of flags. | |
Positional-only parameter. | |
required: bool. If true, exactly one flag must be True. Otherwise, at most | |
one flag can be True, and it is valid for all flags to be False. | |
flag_values: flags.FlagValues, optional FlagValues instance where the flags | |
are defined. | |
Raises: | |
ValueError: Raised when multiple FlagValues are used in the same | |
invocation. This can occur when FlagHolders have different `_flagvalues` | |
or when str-type flag_names entries are present and the `flag_values` | |
argument does not match that of provided FlagHolder(s). | |
""" | |
flag_names, flag_values = _flagvalues.resolve_flag_refs( | |
flag_names, flag_values) | |
for flag_name in flag_names: | |
if not flag_values[flag_name].boolean: | |
raise _exceptions.ValidationError( | |
'Flag --{} is not Boolean, which is required for flags used in ' | |
'mark_bool_flags_as_mutual_exclusive.'.format(flag_name)) | |
def validate_boolean_mutual_exclusion(flags_dict): | |
flag_count = sum(bool(val) for val in flags_dict.values()) | |
if flag_count == 1 or (not required and flag_count == 0): | |
return True | |
raise _exceptions.ValidationError( | |
'{} one of ({}) must be True.'.format( | |
'Exactly' if required else 'At most', ', '.join(flag_names))) | |
register_multi_flags_validator( | |
flag_names, validate_boolean_mutual_exclusion, flag_values=flag_values) | |
def _add_validator(fv, validator_instance): | |
"""Register new flags validator to be checked. | |
Args: | |
fv: flags.FlagValues, the FlagValues instance to add the validator. | |
validator_instance: validators.Validator, the validator to add. | |
Raises: | |
KeyError: Raised when validators work with a non-existing flag. | |
""" | |
for flag_name in validator_instance.get_flags_names(): | |
fv[flag_name].validators.append(validator_instance) | |