diff --git a/changelog/5478.bugfix.rst b/changelog/5478.bugfix.rst new file mode 100644 index 00000000000..74d58e73489 --- /dev/null +++ b/changelog/5478.bugfix.rst @@ -0,0 +1 @@ +Fix encode error when using unicode strings in exceptions with ``pytest.raises``. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8c73ccc6adc..175d6fda01d 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -572,8 +572,13 @@ def match(self, regexp): raised. """ __tracebackhide__ = True - if not re.search(regexp, str(self.value)): - assert 0, "Pattern '{!s}' not found in '{!s}'".format(regexp, self.value) + value = ( + text_type(self.value) if isinstance(regexp, text_type) else str(self.value) + ) + if not re.search(regexp, value): + raise AssertionError( + u"Pattern {!r} not found in {!r}".format(regexp, value) + ) return True diff --git a/testing/python/raises.py b/testing/python/raises.py index cd463d74b07..9cd3ec71734 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -4,6 +4,7 @@ import six import pytest +from _pytest.compat import dummy_context_manager from _pytest.outcomes import Failed from _pytest.warning_types import PytestDeprecationWarning @@ -220,7 +221,7 @@ def test_raises_match(self): int("asdf") msg = "with base 16" - expr = r"Pattern '{}' not found in 'invalid literal for int\(\) with base 10: 'asdf''".format( + expr = r"Pattern '{}' not found in \"invalid literal for int\(\) with base 10: 'asdf'\"".format( msg ) with pytest.raises(AssertionError, match=expr): @@ -278,3 +279,47 @@ def __class__(self): with pytest.raises(CrappyClass()): pass assert "via __class__" in excinfo.value.args[0] + + +class TestUnicodeHandling: + """Test various combinations of bytes and unicode with pytest.raises (#5478) + + https://github.com/pytest-dev/pytest/pull/5479#discussion_r298852433 + """ + + success = dummy_context_manager + py2_only = pytest.mark.skipif( + six.PY3, reason="bytes in raises only supported in Python 2" + ) + + @pytest.mark.parametrize( + "message, match, expectation", + [ + (u"\u2603", u"\u2603", success()), + (u"\u2603", u"\u2603foo", pytest.raises(AssertionError)), + pytest.param(b"hello", b"hello", success(), marks=py2_only), + pytest.param( + b"hello", b"world", pytest.raises(AssertionError), marks=py2_only + ), + pytest.param(u"hello", b"hello", success(), marks=py2_only), + pytest.param( + u"hello", b"world", pytest.raises(AssertionError), marks=py2_only + ), + pytest.param( + u"😊".encode("UTF-8"), + b"world", + pytest.raises(AssertionError), + marks=py2_only, + ), + pytest.param( + u"world", + u"😊".encode("UTF-8"), + pytest.raises(AssertionError), + marks=py2_only, + ), + ], + ) + def test_handling(self, message, match, expectation): + with expectation: + with pytest.raises(RuntimeError, match=match): + raise RuntimeError(message)