Fix decode error in Python 2.7 when docstrings contain a non-ascii character

Fix #628
This commit is contained in:
Bruno Oliveira 2016-01-08 22:19:37 -02:00
parent 29b05c8391
commit 3c19cfcd9a
3 changed files with 69 additions and 8 deletions

View File

@ -19,6 +19,9 @@
properly displayed. properly displayed.
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR. Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
2.8.5 2.8.5
----- -----

View File

@ -81,15 +81,15 @@ class DoctestItem(pytest.Item):
reprlocation = ReprFileLocation(filename, lineno, message) reprlocation = ReprFileLocation(filename, lineno, message)
checker = _get_unicode_checker() checker = _get_unicode_checker()
REPORT_UDIFF = doctest.REPORT_UDIFF REPORT_UDIFF = doctest.REPORT_UDIFF
filelines = py.path.local(filename).readlines(cr=0)
lines = []
if lineno is not None: if lineno is not None:
i = max(test.lineno, max(0, lineno - 10)) # XXX? lines = doctestfailure.test.docstring.splitlines(False)
for line in filelines[i:lineno]: # add line numbers to the left of the error message
lines.append("%03d %s" % (i+1, line)) lines = ["%03d %s" % (i + test.lineno + 1, x)
i += 1 for (i, x) in enumerate(lines)]
# trim docstring error lines to 10
lines = lines[example.lineno - 9:example.lineno + 1]
else: else:
lines.append('EXAMPLE LOCATION UNKNOWN, not showing all tests of that example') lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>' indent = '>>>'
for line in example.source.splitlines(): for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line)) lines.append('??? %s %s' % (indent, line))

View File

@ -1,3 +1,4 @@
# encoding: utf-8
import sys import sys
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
import py import py
@ -109,6 +110,46 @@ class TestDoctests:
"*UNEXPECTED*ZeroDivision*", "*UNEXPECTED*ZeroDivision*",
]) ])
def test_docstring_context_around_error(self, testdir):
"""Test that we show some context before the actual line of a failing
doctest.
"""
testdir.makepyfile('''
def foo():
"""
text-line-1
text-line-2
text-line-3
text-line-4
text-line-5
text-line-6
text-line-7
text-line-8
text-line-9
text-line-10
text-line-11
>>> 1 + 1
3
text-line-after
"""
''')
result = testdir.runpytest('--doctest-modules')
result.stdout.fnmatch_lines([
'*docstring_context_around_error*',
'005*text-line-3',
'006*text-line-4',
'013*text-line-11',
'014*>>> 1 + 1',
'Expected:',
' 3',
'Got:',
' 2',
])
# lines below should be trimmed out
assert 'text-line-2' not in result.stdout.str()
assert 'text-line-after' not in result.stdout.str()
def test_doctest_linedata_missing(self, testdir): def test_doctest_linedata_missing(self, testdir):
testdir.tmpdir.join('hello.py').write(py.code.Source(""" testdir.tmpdir.join('hello.py').write(py.code.Source("""
class Fun(object): class Fun(object):
@ -339,6 +380,23 @@ class TestDoctests:
reprec = testdir.inline_run(p, "--doctest-glob=x*.txt") reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
reprec.assertoutcome(failed=1, passed=0) reprec.assertoutcome(failed=1, passed=0)
def test_contains_unicode(self, testdir):
"""Fix internal error with docstrings containing non-ascii characters.
"""
testdir.makepyfile(u'''
# encoding: utf-8
def foo():
"""
>>> name = 'с' # not letter 'c' but instead Cyrillic 's'.
'anything'
"""
''')
result = testdir.runpytest('--doctest-modules')
result.stdout.fnmatch_lines([
'Got nothing',
'* 1 failed in*',
])
def test_ignore_import_errors_on_doctest(self, testdir): def test_ignore_import_errors_on_doctest(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import asdf import asdf
@ -579,4 +637,4 @@ class TestDoctestAutoUseFixtures:
""") """)
result = testdir.runpytest('--doctest-modules') result = testdir.runpytest('--doctest-modules')
assert 'FAILURES' not in str(result.stdout.str()) assert 'FAILURES' not in str(result.stdout.str())
result.stdout.fnmatch_lines(['*=== 1 passed in *']) result.stdout.fnmatch_lines(['*=== 1 passed in *'])