attrs
attrs copied to clipboard
Create validators not tied to attributes
Currently if I need to add a validator to check a condition that affects two attributes, I have to make it a validator of one of them. For example:
@attr.s
class C(object):
x = attr.ib()
y = attr.ib()
@x.validator
def x_smaller_than_y(self, _ , value):
if value >= self.y:
raise ValueError("'x' has to be smaller than 'y'!")
This feels a little unintuitive because this validator is not really a validator on just 'x'. It should be a validator on 'x' and 'y'. It made me wonder if the order of writing x and y in the class matters, in case the validator is run right after an attribute is set.
I wonder what folks think about a generic validator of this form:
@attr.s
class C(object):
x = attr.ib()
y = attr.ib()
@validator
def x_smaller_than_y(self):
if self.x >= self.y:
raise ValueError("'x' has to be smaller than 'y'!")
This seems like something for __attrs_post_init__ and not a validator?
This may be something to think about in the context of the __setattr__ conversation; presumably this validation should happen any time x or y are assigned to. In which case, it should be separate from __attrs_post_init__.
@wsanchez : What is the __setattr__ conversation?
@hynek : __attrs_post_init__ to me feels like a place to mutate the attrs after initialization, rather than a place to validate them. Since you explicitly have validators, and it is pretty common to want to do multi-attr validation, it seems more natural if you could natively support that. An alternate approach I can think of is some syntax like:
@attr.s
class C(object):
x = attr.ib()
y = attr.ib()
@multi_validator(['x', 'y'])
def x_smaller_than_y(self, x, y):
if x >= y:
raise ValueError("'x' has to be smaller than 'y'!")
@shashankchaudhry Sorry I should have included a pointer. See #645.
Since we are considering validation when attributes are assigned to, then even a validator not specific to a given attribute would probably need to be triggered when you set either any or relevant-to-the-validator attributes. So a solution that only work in the context of the init cycle is probably going to be unsatisfactory.
I’m starting to remember that at some point we had a “validate the whole instance” conversation but it got turned down?