Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom validator in python-attrs with extra parameters

I have several classes defined using attrs like this one:

from attr import attrs, attrib, validators

@attrs
class MyClass:
    name = attrib(])
    @name.validator
    def check_length(self, attribute, value):
        if not (3 <= len(value) <= 30):
            raise ValueError("Name must be between 3 and 30 characters")

    description = attrib()
    @description.validator
    def check_length(self, attribute, value):
        if not (10 <= len(value) <= 400):
            raise ValueError("Description must be between 10 and 400 characters")

For several attributes I need to create a validator to check wether the data it's in some range. I want to avoid repetition, so I can create a custom validator where I pass some extra arguments for min and max, like this one:

def range_validator(instance, attribute, value, min_value, max_value):
    if  min_value >= len(value) >= max_value:
        raise ValueError("Must be between {} and {}".format(min_value, max_value))

But then I don't know how to call this validator from inside attrib() passing the extra args (min_value and max_value), I mean do something like:

name = attrib(validator=[range_validator(self, 10, 30)])
like image 229
fasouto Avatar asked Oct 18 '25 06:10

fasouto


1 Answers

You could use functools.partial:

def range_validator(instance, attribute, value, min_value, max_value):
    lv = len(value)
    if min_value > lv or lv > max_value:
        raise ValueError("Must be between {} and {}".format(min_value, max_value))

@attrs
class C:
    x = attrib(validator=partial(range_validator, min_value=10, max_value=30))

Alternatively you can use a closure:

def make_range_validator(min_value, max_value):
    def range_validator(instance, attribute, value):
        lv = len(value)
        if min_value > lv or lv > max_value:
            raise ValueError("Must be between {} and {}".format(min_value, max_value))

    return range_validator

@attrs
class C:
    x = attrib(validator=make_range_validator(10, 30))

I personally like the closure factory approach better because they are more explicit about what you’re doing. Partials always feel a bit ad-hoc to me, but that might just be me.

(Please note that I took the liberty to fix a logic bug in your validator – you may want to apply it too. :))

like image 162
hynek Avatar answered Oct 20 '25 21:10

hynek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!