Skip to content

Commit

Permalink
Improve the error message informing the type that it got it
Browse files Browse the repository at this point in the history
  • Loading branch information
williamjamir committed May 30, 2019
1 parent 45adbb9 commit 7efdc3c
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ Validators
>>> C("not a callable")
Traceback (most recent call last):
...
TypeError: 'x' must be callable
TypeError: 'x' must be callable (got 'not a callable' that is a <class 'str'>).


.. autofunction:: attr.validators.deep_iterable
Expand Down
10 changes: 8 additions & 2 deletions src/attr/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,12 @@ def __call__(self, inst, attr, value):
We use a callable class to be able to change the ``__repr__``.
"""
if not callable(value):
raise TypeError("'{name}' must be callable".format(name=attr.name))
raise TypeError(
"'{name}' must be callable (got {value!r} that is a "
"{actual!r}).".format(
name=attr.name, value=value, actual=value.__class__
)
)

def __repr__(self):
return "<is_callable validator>"
Expand All @@ -200,7 +205,8 @@ def is_callable():
.. versionadded:: 19.1.0
:raises TypeError: With a human readable error message containing the
attribute (of type :class:`attr.Attribute`) name.
attribute (of type :class:`attr.Attribute`) name, and the value it
got.
"""
return _IsCallableValidator()

Expand Down
25 changes: 21 additions & 4 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,13 @@ def test_noncallable_validators(
"""
with pytest.raises(TypeError) as e:
deep_iterable(member_validator, iterable_validator)

e.match(r"\w* must be callable")
value = 42
assert (
"must be callable (got {value} that is a {type_}).".format(
value=value, type_=value.__class__
)
in e.value.args[0]
)

def test_fail_invalid_member(self):
"""
Expand Down Expand Up @@ -466,7 +471,13 @@ def test_noncallable_validators(
with pytest.raises(TypeError) as e:
deep_mapping(key_validator, value_validator, mapping_validator)

e.match(r"\w* must be callable")
value = 42
assert (
"must be callable (got {value} that is a {type_}).".format(
value=value, type_=value.__class__
)
in e.value.args[0]
)

def test_fail_invalid_mapping(self):
"""
Expand Down Expand Up @@ -550,7 +561,13 @@ def test_fail(self):
with pytest.raises(TypeError) as e:
v(None, a, None)

e.match("'test' must be callable")
value = None
assert (
"must be callable (got {value} that is a {type_}).".format(
value=value, type_=value.__class__
)
in e.value.args[0]
)

def test_repr(self):
"""
Expand Down

0 comments on commit 7efdc3c

Please sign in to comment.