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 valid Path 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 internal Interface 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 the validate_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, where None is a special value used to indicate an optional and unset value, but actually only paths are acceptable input values. can simply use EnsurePath() and it is not necessary to do something like EnsurePath() | 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 a CommandParametrizationError 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 the validate_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 a ParameterConstraintContext 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 to EnsureCommandParameterization.

Any raised ConstraintError is caught and reported together with the respective ParameterConstraintContext. The violating value reported in such a ConstraintError must be a mapping of parameter name to value, comprising the full parameter set (i.e., keys matching the ParameterConstraintContext). The use of self.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.