Skip to content

Commit dca77b2

Browse files
committed
Escape whitespace only strings when diffing them on failed assertions
Fix #3443
1 parent 08aed1a commit dca77b2

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

_pytest/assertion/util.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,22 @@ def _diff_text(left, right, verbose=False):
171171
"""
172172
from difflib import ndiff
173173
explanation = []
174+
175+
def to_unicode_text(binary_text):
176+
"""
177+
This ensures that the internal string is always valid unicode, converting any bytes safely to valid unicode.
178+
This is done using repr() which then needs post-processing to fix the encompassing quotes and un-escape
179+
newlines and carriage returns (#429).
180+
"""
181+
r = six.text_type(repr(binary_text)[1:-1])
182+
r = r.replace(r'\n', '\n')
183+
r = r.replace(r'\r', '\r')
184+
return r
185+
174186
if isinstance(left, six.binary_type):
175-
left = u(repr(left)[1:-1]).replace(r'\n', '\n')
187+
left = to_unicode_text(left)
176188
if isinstance(right, six.binary_type):
177-
right = u(repr(right)[1:-1]).replace(r'\n', '\n')
189+
right = to_unicode_text(right)
178190
if not verbose:
179191
i = 0 # just in case left or right has zero length
180192
for i in range(min(len(left), len(right))):
@@ -197,6 +209,10 @@ def _diff_text(left, right, verbose=False):
197209
left = left[:-i]
198210
right = right[:-i]
199211
keepends = True
212+
if left.isspace() or right.isspace():
213+
left = repr(str(left))
214+
right = repr(str(right))
215+
explanation += [u'Strings contain only whitespace, escaping them using repr()']
200216
explanation += [line.strip('\n')
201217
for line in ndiff(left.splitlines(keepends),
202218
right.splitlines(keepends))]

changelog/3443.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When showing diffs of failed assertions where the contents contain only whitespace, escape them using ``repr()`` first to make it easy to spot the differences.

testing/test_assertion.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,18 @@ def test_reprcompare_notin(mock_config):
746746
assert detail == ["'foo' is contained here:", ' aaafoobbb', '? +++']
747747

748748

749+
def test_reprcompare_whitespaces(mock_config):
750+
detail = plugin.pytest_assertrepr_compare(
751+
mock_config, '==', '\r\n', '\n')
752+
assert detail == [
753+
r"'\r\n' == '\n'",
754+
r"Strings contain only whitespace, escaping them using repr()",
755+
r"- '\r\n'",
756+
r"? --",
757+
r"+ '\n'",
758+
]
759+
760+
749761
def test_pytest_assertrepr_compare_integration(testdir):
750762
testdir.makepyfile("""
751763
def test_hello():

0 commit comments

Comments
 (0)