datalad-next's Constraint System

datalad_next.constraints implements a system to perform data validation, coercion, and parameter documentation for commands via a flexible set of "Constraints". You can find an overview of available Constraints in the respective module overview of the Python tooling.

Adding parameter validation to a command

In order to equip an existing or new command with the constraint system, the following steps are required:

  • Set the commands base class to ValidatedInterface:

from datalad_next.commands import ValidatedInterface

@build_doc
class MyCommand(ValidatedInterface):
    """Download from URLs"""
  • Declare a _validator_ class member:

from datalad_next.commands import (
    EnsureCommandParameterization,
    ValidatedInterface,
)

@build_doc
class MyCommand(ValidatedInterface):
    """Download from URLs"""

_validator_ = EnsureCommandParameterization(dict(
     [...]
 ))
  • Determine for each parameter of the command whether it has constraints, and what those constraints are. If you're transitioning an existing command, remove any constraints= declaration in the _parameter_ class member.

  • Add a fitting Constraint declaration for each parameter into the _validator_ as a key-value pair where the key is the parameter and its value is a Constraint. There does not need to be a Constraint per parameter; only add entries for parameters that need validation.

from datalad_next.commands import (
    EnsureCommandParameterization,
    ValidatedInterface,
)
from datalad_next.constraints import EnsureChoice
from datalad_next.constraints import EnsureDataset

@build_doc
class Download(ValidatedInterface):
    """Download from URLs"""

_validator_ = EnsureCommandParameterization(dict(
     dataset=EnsureDataset(installed=True),
     force=EnsureChoice('yes','no','maybe'),
 ))

Combining constraints

Constraints can be combined in different ways. The |, &, and () operators allow AND, OR, and grouping of Constraints. The following example from the download command defines a chain of possible Constraints:

spec_item_constraint = url2path_constraint | (
     (
         EnsureJSON() | EnsureURLFilenamePairFromURL()
     ) & url2path_constraint)

Constrains can also be combined using AnyOf or AllOf MultiConstraints, which correspond almost entirely to | and &. Here's another example from the download command:

spec_constraint = AnyOf(
    spec_item_constraint,
    EnsureListOf(spec_item_constraint),
    EnsureGeneratorFromFileLike(
        spec_item_constraint,
        exc_mode='yield',
    ),

One can combine an arbitrary number of Constraints. They are evaluated in the order in which they were specified. Logical OR constraints will return the value from the first constraint that does not raise an exception, and logical AND constraints pass the return values of each constraint into the next.

Implementing additional constraints

TODO

Parameter Documentation

TODO