attrs icon indicating copy to clipboard operation
attrs copied to clipboard

attr.validate() is not recursive

Open dcczi opened this issue 5 years ago • 1 comments

I am using attrs to manage the configuration of a database system. I was hoping to use set_run_validators to temporarily disable validation during a multi-step evolution of a large data structure composed of nested attr classes, and deferring the validators until all the evolutions are complete.

It surprised me to see that attrs.validate is not recursive -- it only invokes the validators of the top-level fields of a class, and won't invoke the attr validators on any attr instances on that field.

The wording in the docs is technically correct but a rewording may be helpful.

Also, would there be any reason not to add something like a recursive=True option?

The script below illustrates the behavior.

def log_validation(cls, _, _2):
    print(f"in log_validation for {cls}")


@attr.s(frozen=True)
class Inner:
    vi: int = attr.ib(validator=log_validation)

@attr.s(frozen=True)
class Outer:
    vo: str = attr.ib(validator=log_validation)
    i: Inner = attr.ib()

attr.set_run_validators(False)
x = Outer(vo=None, i=Inner(vi=123))
attr.set_run_validators(True)

attr.validate(x)
# output:
# in log_validation for Inner(vi=123)

My assumption is that it would validate the entire tree, much as we would get if we serialized and deserialized the instance:

cattr.structure(cattr.unstructure(x), Outer)
# output:
# in log_validation for Inner(vi=123)
# in log_validation for Outer(vo='None', i=Inner(vi=123))

I'm using attrs 19.3.0.

Thanks!

dcczi avatar Dec 02 '20 23:12 dcczi

I think your assumption is correct given the name. attr.validate was really just an explicit way to running validators, not the way you use it. While I think validation is usually more complicated and should be done thru one of the numerous frameworks, I think that would be a reasonable feature and I'd merge a good PR that added it.

hynek avatar Dec 13 '20 15:12 hynek