diff --git a/src/attr/_funcs.py b/src/attr/_funcs.py index 7eaabf245..1213979ce 100644 --- a/src/attr/_funcs.py +++ b/src/attr/_funcs.py @@ -201,7 +201,8 @@ def evolve(inst, **changes): .. versionadded:: 17.1.0 """ cls = inst.__class__ - for a in fields(cls): + attrs = fields(cls) + for a in attrs: attr_name = a.name # To deal with private attributes. if attr_name[0] == "_": init_name = attr_name[1:] @@ -216,6 +217,9 @@ def evolve(inst, **changes): try: return cls(**changes) except TypeError as exc: - k = exc.args[0].split("'")[1] - raise AttrsAttributeNotFoundError( - "{k} is not an attrs attribute on {cl}.".format(k=k, cl=cls)) + for name in changes: + if getattr(attrs, name, None) is None: + k = exc.args[0].split("'")[1] + raise AttrsAttributeNotFoundError( + "{} is not an attrs attribute on {}.".format(k, cls)) + raise diff --git a/tests/test_funcs.py b/tests/test_funcs.py index eeba2e198..60dc4d9b4 100644 --- a/tests/test_funcs.py +++ b/tests/test_funcs.py @@ -24,6 +24,9 @@ ) from attr.exceptions import AttrsAttributeNotFoundError +from attr.validators import instance_of +from attr._compat import TYPE + MAPPING_TYPES = (dict, OrderedDict) SEQUENCE_TYPES = (list, tuple) @@ -464,3 +467,16 @@ def test_unknown(self, C): assert ( "aaaa is not an attrs attribute on {cls!r}.".format(cls=C), ) == e.value.args + + + def test_validator_failure(self): + """ + Make sure we don't swallow TypeError when validation fails within evolve + """ + @attributes + class C(object): + a = attr(validator=instance_of(int)) + + with pytest.raises(TypeError) as e: + evolve(C(a=1), a="some string") + assert e.value.args[0].startswith("'a' must be <{type} 'int'>".format(type=TYPE))