|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""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) |
|
|
|