diff --git a/AUTHORS b/AUTHORS index ad9541423d2..eb3c016a570 100644 --- a/AUTHORS +++ b/AUTHORS @@ -188,6 +188,7 @@ Tareq Alayan Ted Xiao Thomas Grainger Thomas Hisch +Tim Strazny Tom Dalton Tom Viner Trevor Bekolay diff --git a/_pytest/python_api.py b/_pytest/python_api.py index 0b30b7ac8e7..8e09a4a6fd3 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -2,6 +2,7 @@ import sys import py +from six import binary_type, text_type from six.moves import zip, filterfalse from more_itertools.more import always_iterable @@ -584,7 +585,8 @@ def raises(expected_exception, *args, **kwargs): """ __tracebackhide__ = True - for exc in filterfalse(isclass, always_iterable(expected_exception)): + base_type = (type, text_type, binary_type) + for exc in filterfalse(isclass, always_iterable(expected_exception, base_type)): msg = ("exceptions must be old-style classes or" " derived from BaseException, not %s") raise TypeError(msg % type(exc)) diff --git a/changelog/3372.bugfix.rst b/changelog/3372.bugfix.rst new file mode 100644 index 00000000000..722bdab1e6e --- /dev/null +++ b/changelog/3372.bugfix.rst @@ -0,0 +1 @@ +``pytest.raises`` now works with exception classes that look like iterables. diff --git a/testing/python/raises.py b/testing/python/raises.py index 156319816ea..053426395d0 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -1,3 +1,4 @@ +from _pytest.outcomes import Failed import pytest import sys @@ -147,3 +148,20 @@ def test_raises_match_wrong_type(self): with pytest.raises(ValueError): with pytest.raises(IndexError, match='nomatch'): int('asdf') + + def test_raises_exception_looks_iterable(self): + from six import add_metaclass + + class Meta(type(object)): + def __getitem__(self, item): + return 1/0 + + def __len__(self): + return 1 + + @add_metaclass(Meta) + class ClassLooksIterableException(Exception): + pass + + with pytest.raises(Failed, match="DID NOT RAISE "): + pytest.raises(ClassLooksIterableException, lambda: None)