Skip to content

Commit

Permalink
Merge pull request #4001 from asottile/fix_bytes_repr_text_mix_python_2
Browse files Browse the repository at this point in the history
Fix UnicodeDecodeError in assertion with mixed non-ascii bytes repr + text
  • Loading branch information
RonnyPfannschmidt authored Sep 20, 2018
2 parents 7a5e11b + 7122fa5 commit f6eb39d
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog/3999.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text.
13 changes: 9 additions & 4 deletions src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os
import re
import six
import string
import struct
import sys
import types
Expand Down Expand Up @@ -466,10 +467,14 @@ def _saferepr(obj):
"""
r = py.io.saferepr(obj)
if isinstance(r, six.text_type):
return r.replace(u"\n", u"\\n")
else:
return r.replace(b"\n", b"\\n")
# only occurs in python2.x, repr must return text in python3+
if isinstance(r, bytes):
# Represent unprintable bytes as `\x##`
r = u"".join(
u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
for c in r
)
return r.replace(u"\n", u"\\n")


from _pytest.assertion.util import format_explanation as _format_explanation # noqa
Expand Down
18 changes: 17 additions & 1 deletion testing/test_assertrewrite.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function

import glob
Expand Down Expand Up @@ -57,7 +58,7 @@ def getmsg(f, extra_ns=None, must_pass=False):
except AssertionError:
if must_pass:
pytest.fail("shouldn't have raised")
s = str(sys.exc_info()[1])
s = six.text_type(sys.exc_info()[1])
if not s.startswith("assert"):
return "AssertionError: " + s
return s
Expand Down Expand Up @@ -608,6 +609,21 @@ def __repr__(self):

assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]

def test_custom_repr_non_ascii(self):
def f():
class A(object):
name = u"ä"

def __repr__(self):
return self.name.encode("UTF-8") # only legal in python2

a = A()
assert not a.name

msg = getmsg(f)
assert "UnicodeDecodeError" not in msg
assert "UnicodeEncodeError" not in msg


class TestRewriteOnImport(object):
def test_pycache_is_a_file(self, testdir):
Expand Down

0 comments on commit f6eb39d

Please sign in to comment.