datalad_next.constraints.EnsureCommandParameterization
- class datalad_next.constraints.EnsureCommandParameterization(param_constraints: Dict[str, Constraint], *, validate_defaults: Container[str] | None = None, joint_constraints: Dict[ParameterConstraintContext, Callable] | None = None, tailor_for_dataset: Dict[str, str] | None = None)[source]
Bases:
Constraint
Base class for ValidatedInterface parameter validators
This class can be used as-is, by declaring individual constraints in the constructor, or it can be subclassed to consolidate all custom validation-related code for a command in a single place.
Commonly this constraint is used by declaring particular value constraints for individual parameters as a mapping. Declaring that the
path
parameter should receive something that is or can be coerced to a validPath
object looks like this:EnsureCommandParameterization({'path': EnsurePath()})
This class differs from a standard
Constraint
implementation, because its__call__()
method support additional arguments that are used by the internalInterface
handling code to control how parameters are validated.During validation, when no validator for a particular parameter is declared, any input value is passed on as-is, and otherwise an input is passed through the validator.
There is one exception to this rule: When a parameter value is identical to its default value (as declared in the command signature, and communicated via the
at_default
argument of__call__()
), this default value is also passed as-is, unless the respective parameter name is included in thevalidate_defaults
constructor argument.An important consequence of this behavior is that validators need not cover a default value. For example, a parameter constraint for
path=None
, whereNone
is a special value used to indicate an optional and unset value, but actually only paths are acceptable input values. can simply useEnsurePath()
and it is not necessary to do something likeEnsurePath() | EnsureNone()
.However, EnsureCommandParameterization can also be specifically instructed to perform validation of defaults for individual parameters, as described above. A common use case is the auto-discovery of datasets, where often None is the default value of a dataset parameter (to make it optional), and an EnsureDataset constraint is used. This constraint can perform the auto-discovery (with the None value indicating that), but validation of defaults must be turned on for the dataset parameter in order to do that.
A second difference to a common
Constraint
implementation is the ability to perform an "exhaustive validation" on request (via__call__(on_error=...)
). In this case, validation is not stopped at the first discovered violation, but all violations are collected and communicated by raising aCommandParametrizationError
exception, which can be inspected by a caller for details on number and nature of all discovered violations.Exhaustive validation and joint reporting are only supported for individual constraint implementations that raise ConstraintError exceptions. For legacy constraints, any raised exception of another type are not caught and reraised immediately.
- __call__(kwargs, at_default=None, required=None, on_error='raise-early') Dict [source]
- Parameters:
kwargs (dict) -- Parameter name (
str
)) to value (any) mapping of the parameter set.at_default (set or None) -- Set of parameter names where the respective values in
kwargs
match their respective defaults. This is used for deciding whether or not to process them with an associated value constraint (see thevalidate_defaults
constructor argument).required (set or None) -- Set of parameter names that are known to be required.
on_error ({'raise-early', 'raise-at-end'}) -- Flag how to handle constraint violation. By default, validation is stopped at the first error and an exception is raised. When an exhaustive validation is performed, an eventual exception contains information on all constraint violations. Regardless of this mode more than one error can be reported (in case (future) implementation perform independent validations in parallel).
- Raises:
CommandParametrizationError -- Raised whenever one (or more)
ConstraintError
exceptions are caught during validation. Other exception types are not caught and pass through.
- joint_validation(params: Dict, on_error: str) Dict [source]
Higher-order validation considering multiple parameters at a time
This method is called with all, individually validated, command parameters in keyword-argument form in the
params
dict argument.Arbitrary additional validation steps can be performed on the full set of parameters that may involve raising exceptions on validation errors, but also value transformation or replacements of individual parameters based on the setting of others.
The parameter values returned by the method are passed on to the respective command implementation.
The default implementation iterates over the
joint_validators
specification given to the constructor, in order to perform any number of validations. This is a mapping of aParameterConstraintContext
instance to a callable implementing a validation for a particular parameter set.Example:
_joint_validators_ = { ParameterConstraintContext(('p1', 'p2'), 'sum'): MyValidator._check_sum, } def _checksum(self, p1, p2): if (p1 + p2) < 3: self.raise_for( dict(p1=p1, p2=p2), 'parameter sum is too large', )
The callable will be passed the arguments named in the
ParameterConstraintContext
as keyword arguments, using the same names as originally given toEnsureCommandParameterization
.Any raised
ConstraintError
is caught and reported together with the respectiveParameterConstraintContext
. The violating value reported in such aConstraintError
must be a mapping of parameter name to value, comprising the full parameter set (i.e., keys matching theParameterConstraintContext
). The use ofself.raise_for()
is encouraged.If the callable anyhow modifies the passed arguments, it must return them as a kwargs-like mapping. If nothing is modified, it is OK to return
None
.- Returns:
dict -- The returned dict must have a value for each item passed in via
params
.on_error ({'raise-early', 'raise-at-end'}) -- Flag how to handle constraint violation. By default, validation is stopped at the first error and an exception is raised. When an exhaustive validation is performed, an eventual exception contains information on all constraint violations.
- Raises:
ConstraintErrors -- With on_error='raise-at-end' an implementation can choose to collect more than one higher-order violation and raise them as a ConstraintErrors exception.