-
-
Notifications
You must be signed in to change notification settings - Fork 374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
validation triggered too late #461
Comments
This is expected behaviour. As stated at http://www.attrs.org/en/stable/init.html?highlight=order#callables
As defaults are evaluated during initialization, the |
Yes this is indeed on purpose and simply a trade off call we had to make. I believe the way we chose to operate is overall more flexible. |
Documentation would benefit from one clear statement about order of particular function execution. Currently one has to hunt it in multiple places. Here is my proposal with demonstration about how it really works: """Order of applying attribute default, converter and validator
For each attribute one by one: apply default, then converter.
For each attribute: apply validator
For the instance, call `__attrs_post_init__`
"""
from functools import partial
import attr
from structlog import get_logger
log = get_logger()
def str2int(attname, text):
log.info("converter", attname=attname)
return text
def def_factory(attname):
log.info("def_factory", attname=attname)
return 123
def is_positive(attname, instance, attribute, value):
log.info("validator", attname=attname)
if value <= 0:
raise ValueError("Value is not positive")
@attr.s
class A:
a: int = attr.ib(
default=attr.Factory(partial(def_factory, "a")),
converter=partial(str2int, "a"),
validator=partial(is_positive, "a"),
)
b: int = attr.ib(
default=attr.Factory(partial(def_factory, "b")),
converter=partial(str2int, "b"),
validator=partial(is_positive, "b"),
)
da: int = attr.ib(converter=partial(str2int, "da"))
db: int = attr.ib(converter=partial(str2int, "db"))
def __attrs_post_init__(self):
log.info("within __attrs_post_init__")
@da.default
def _da_default(self):
log.info("default", attname="da")
return 24
@da.validator
def _da_validator(self, attribute, value):
log.info("validator", attname="da")
if value <= 0:
raise ValueError("Value is not positive")
@db.default
def _db_default(self):
log.info("default", attname="db")
return 24
@db.validator
def _db_validator(self, attribute, value):
log.info("validator", attname="db")
if value <= 0:
raise ValueError("Value is not positive")
def test_it():
inst = A()
print("inst", inst) Running it (as a pytest driven test case) results in following output:
|
@vlcinsky Tnx. Cristal clear explanation |
* Clarify execution order in init Fixes #461 * Clarify attributes are processed in the order of declaration * Simple past is good enough
attrs
18.2.0
python
3.6.7
I'm not sure, is it bug or expected behavior, but steps to reproduce:
Expected(by me):
Real:
The text was updated successfully, but these errors were encountered: