diff --git a/CHANGELOG b/CHANGELOG index 8fe13a643..07da9a1e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,9 @@ NEXT - fixed issue561: adapt autouse fixture example for python3. +- fixed issue453: assertion rewriting issue with __repr__ containing + "\n{", "\n}" and "\n~". + - Fix example in monkeypatch documentation, thanks t-8ch. - Do not mark as universal wheel because Python 2.6 is different from diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index 523d2b2dc..cb8acc187 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -326,7 +326,25 @@ def rewrite_asserts(mod): AssertionRewriter().run(mod) -_saferepr = py.io.saferepr +def _saferepr(obj): + """Get a safe repr of an object for assertion error messages. + + The assertion formatting (util.format_explanation()) requires + newlines to be escaped since they are a special character for it. + Normally assertion.util.format_explanation() does this but for a + custom repr it is possible to contain one of the special escape + sequences, especially '\n{' and '\n}' are likely to be present in + JSON reprs. + + """ + repr = py.io.saferepr(obj) + if py.builtin._istext(repr): + t = py.builtin.text + else: + t = py.builtin.bytes + return repr.replace(t("\n"), t("\\n")) + + from _pytest.assertion.util import format_explanation as _format_explanation # noqa def _should_repr_global_name(obj): diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 72e2d4e10..7e0cfbf83 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -313,6 +313,17 @@ class TestAssertionRewrite: assert "%test" == "test" assert getmsg(f).startswith("assert '%test' == 'test'") + def test_custom_repr(self): + def f(): + class Foo(object): + a = 1 + + def __repr__(self): + return "\n{ \n~ \n}" + f = Foo() + assert 0 == f.a + assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0] + class TestRewriteOnImport: