attrs
attrs copied to clipboard
attr.validate() is not recursive
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!
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.