It can be useful to restrict the set of that values that a parameter node parameter can be set to. These can be done with Validator annotations.

from typing import Annotated
from slicer.parameterNodeWrapper import parameterNodeWrapper, Minimum, Default

class CustomParameterNode:
  numIterations: Annotated[int, Minimum(0)] = 500

  # To have a list where the values in the list need to be validated
  chosenFeatures: list[Annotated[str, Choice(["feat1", "feat2", "feat3"])]]

This will cause a ValueError to be raised if someone tried setting numIterations to a negative value.

Multiple validators can be placed in the Annotated block and they will be run in the order they were placed.

Built-in validators

The list of built-in validators is as follows:

Class name Description
NotNone() Ensures the value is not None.
IsInstance(classtype)/IsInstance((class1type, class2type)) Ensures the value is an instance of particular type or set of types.
Minimum(value) Ensures the value is greater than or equal to the given value.
Maximum(value) Ensures the value is less than or equal to the given value.
WithinRange(minimum, maximum) Ensures the value is within the range given by minimum and maximum, inclusive.
Choice(validChoices) Ensures the value is contained in the given list of valid choices. validChoices can be any iterable.
Exclude(invalidChoices) Ensures the value is not contained in the given list of invalid choices. invalidChoices can be any iterable.

Some built-in types have the following validators applied to them by default:

Type Default validators
int, str, bool, any of the pathlib path types NotNone(), IsInstance(<type>)
float NotNone(), IsInstance((float, int)) (this allows implicit conversion from int)
vtkMRMLModelNode (and subclasses) IsInstance(<type>)

Custom Validators

Custom validators can easily be created and used with the parameterNodeWrapper.

import re
from typing import Annotated
from slicer.parameterNodeWrapper import parameterNodeWrapper, Validator

# Custom validators must derive from the Validator class.
class MatchesRegex(Validator):
  def __init__(self, regex):
    self.regex = regex
  # Custom validators must implement a validate function that raises an Exception
  # if the given value is invalid.
  def validate(self, value):
    if re.match(self.regex, value) is None:
      raise ValueError("Did not match regex")

class CustomParameterNode:
  value: Annotated[str, MatchesRegex("[abc]+")] = "abcba"

param = CustomParameterNode(slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScriptedModuleNode'))

param.value = "abcabc" # ok
param.value = "d" # ValueError raised