brillenheini
brillenheini

Reputation: 963

Why can't I assign values to an attribute after creating an instance of class?

I just discovered attrs and it's actually great for my upcoming project. While playing around I recognized some behaviour which I can't explain to myself.

I have this piece of code:

from attr import attrs, attrib, Factory

def validate_dict(instance, attribute, value):
    if not isinstance(value, dict):
        raise ValueError(f"Attribute `{attribute.name}` has to be of type dict(), not {type(value)}!")

@attrs(kw_only=True, on_setattr=validate_dict)
class FooBar:
    defaults = attrib(default=Factory(dict), validator=validate_dict)
    config = attrib(default=Factory(dict), validator=validate_dict)
    source = attrib(default=Factory(dict), validator=validate_dict)
    target = attrib(default=Factory(dict), validator=validate_dict)

>>> foobar = FooBar()
>>> foobar
FooBar(defaults={}, config={}, source={}, target={})
>>> foobar.defaults = {'firstname':'Thomas'}
>>> foobar
FooBar(defaults=None, config={}, source={}, target={})

Using foobar.defaults.update(firstname='Thomas') works, also foobar = FooBar(defaults={'firstname':'Thomas'}), but shouldn't work direct assigning as well? Or is the way I use on_setattr and validator wrong?

When I try to set an attribute with an int, i.e. foobar.defaults = 1 the ValueError is correctly raised.

Any hints appreciated. Regards, Thomas

Upvotes: 0

Views: 866

Answers (1)

BrenBarn
BrenBarn

Reputation: 251365

According to the documentation for on_setattr:

If no exception is raised, the attribute is set to the return value of the callable.

Your validate_dict returns None, so that is what is being set as the value. If you want to set the value to the dict passed in, you need to do return value in validate_dict. (Presumably the API was set up this way so that such a handler can modify the passed-in value rather than simply accepting or rejecting it.)

Upvotes: 1

Related Questions