What is the best way to make custom validation and transform before validation

Hi there, I am just starting to get quite fond the really nice possibilities the param library has to offer :smile:

To give some context, I would like to make the param.Color parameter also accept also short color names. This implies that short color names must be “translated” into valid color names before setting the value.

I am currently trying to customize some validation behavior but I could not find a “proper” way to do it in the documentation. So far I found 2 possibility to implement input transformation before validation but I am not sure either of those is the “right way”.

I would be already greatful if someone could point me in the right direction. Thanks :wink:

Solution 1: Subclassing param.Color and intercept __set__

import param

SHORT_COLOR_MAPPING = {
    "r": "red",
    "g": "green",
    "b": "blue",
    "y": "yellow",
    "m": "magenta",
    "c": "cyan",
    "k": "black",
    "w": "white",
}

class MyParamColor(param.Color):
    
    def __set__(self, obj, val):
        val = SHORT_COLOR_MAPPING.get(val, val)
        super().__set__( obj, val)
        
class Box(param.Parameterized):
    color = MyParamColor()
    
mybox = Box()
mybox.color='r'
mybox.color
# [out] 'red'

Solution 2: Intercept __setattr__ on the main class where param.Color is defined

import param

SHORT_COLOR_MAPPING = {
    "r": "red",
    "g": "green",
    "b": "blue",
    "y": "yellow",
    "m": "magenta",
    "c": "cyan",
    "k": "black",
    "w": "white",
}

class Box(param.Parameterized):
    color = param.Color()

    def __setattr__(self, name, value):
        if name=='color':
            value = SHORT_COLOR_MAPPING.get(value, value)
        super().__setattr__(name, value)
    
mybox = Box()
mybox.color='r'
mybox.color
# [out] 'red'
1 Like

Listening in. Would also like to learn the best practice for cleaning and validation.

It’s definitely not a good idea to override setattr on the Parameterized class, because then your abbreviation will only apply once the parameter is “owned” by a Parameterized, preventing you from using the value in a Parameter constructor. So I’d reserve that for a validation or transform that requires access to the Parameterized, e.g. to check values of other parameters (which is generally a bad idea anyway, and typically works better as param.depends).

Here, there’s no need to bring Parameterized into it, so I’d go with your first approach, if indeed what you want is a transformation. If all you want is validation (leaving the actual value to be “r” and not “red”), then you’d reimplement the most specific _validate method involved. Here, Color._validate calls Color_._validate_allow_named to check for color names, so that’s what to reimplement to get validation without transformation:

import param

SHORT_COLOR_MAPPING = {
    "r": "red",
    "g": "green",
    "b": "blue",
    "y": "yellow",
    "m": "magenta",
    "c": "cyan",
    "k": "black",
    "w": "white",
}

class MyParamColor(param.Color):
    def _validate_allow_named(self, val, allow_named):
        val = SHORT_COLOR_MAPPING.get(val, val)
        super()._validate_allow_named(val, allow_named)

class Box(param.Parameterized):
    color = MyParamColor()

mybox = Box()
mybox.color='r'
mybox.color

But I’d guess that transformation is what serves you best here, so go with solution 1.

I’ve been thinking about this for a while. Does Param not lack an official api for cleaning and validating input? @jbednar

This section sort of describes custom validation. Parameters and Parameterized objects — param 1.12.0.post3+g78e2ea4 documentation

There is definitely a need for this for yet another reason. Even if a value passes the parameter’s validation checks you may want to preprocess/transform it before you trigger downstream events with that value. I originally wrote that up as an issue here: Add concept of parameter preprocessor or allow invalidating watch events · Issue #324 · holoviz/param · GitHub

Yes, I’d be happy to see a PR adding a “transform” slot that would accept a callable to filter the value before validating or setting. This is related to the existing set_in_bounds support, but more general.