Skip to content

Commit cf28c61

Browse files
[3.11] gh-111159: Fix doctest output comparison for exceptions with notes (GH-111160) (#111170)
gh-111159: Fix `doctest` output comparison for exceptions with notes (GH-111160) (cherry picked from commit fd60549) Co-authored-by: Nikita Sobolev <[email protected]>
1 parent 4222dd9 commit cf28c61

File tree

3 files changed

+159
-1
lines changed

3 files changed

+159
-1
lines changed

Lib/doctest.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1370,7 +1370,20 @@ def __run(self, test, compileflags, out):
13701370

13711371
# The example raised an exception: check if it was expected.
13721372
else:
1373-
exc_msg = traceback.format_exception_only(*exception[:2])[-1]
1373+
formatted_ex = traceback.format_exception_only(*exception[:2])
1374+
if issubclass(exception[0], SyntaxError):
1375+
# SyntaxError / IndentationError is special:
1376+
# we don't care about the carets / suggestions / etc
1377+
# We only care about the error message and notes.
1378+
# They start with `SyntaxError:` (or any other class name)
1379+
exc_msg_index = next(
1380+
index
1381+
for index, line in enumerate(formatted_ex)
1382+
if line.startswith(f"{exception[0].__name__}:")
1383+
)
1384+
formatted_ex = formatted_ex[exc_msg_index:]
1385+
1386+
exc_msg = "".join(formatted_ex)
13741387
if not quiet:
13751388
got += _exception_traceback(exception)
13761389

Lib/test/test_doctest.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,150 @@ def test_run_doctestsuite_multiple_times():
31683168
"""
31693169

31703170

3171+
def test_exception_with_note(note):
3172+
"""
3173+
>>> test_exception_with_note('Note')
3174+
Traceback (most recent call last):
3175+
...
3176+
ValueError: Text
3177+
Note
3178+
3179+
>>> test_exception_with_note('Note') # doctest: +IGNORE_EXCEPTION_DETAIL
3180+
Traceback (most recent call last):
3181+
...
3182+
ValueError: Text
3183+
Note
3184+
3185+
>>> test_exception_with_note('''Note
3186+
... multiline
3187+
... example''')
3188+
Traceback (most recent call last):
3189+
ValueError: Text
3190+
Note
3191+
multiline
3192+
example
3193+
3194+
Different note will fail the test:
3195+
3196+
>>> def f(x):
3197+
... r'''
3198+
... >>> exc = ValueError('message')
3199+
... >>> exc.add_note('note')
3200+
... >>> raise exc
3201+
... Traceback (most recent call last):
3202+
... ValueError: message
3203+
... wrong note
3204+
... '''
3205+
>>> test = doctest.DocTestFinder().find(f)[0]
3206+
>>> doctest.DocTestRunner(verbose=False).run(test)
3207+
... # doctest: +ELLIPSIS
3208+
**********************************************************************
3209+
File "...", line 5, in f
3210+
Failed example:
3211+
raise exc
3212+
Expected:
3213+
Traceback (most recent call last):
3214+
ValueError: message
3215+
wrong note
3216+
Got:
3217+
Traceback (most recent call last):
3218+
...
3219+
ValueError: message
3220+
note
3221+
TestResults(failed=1, attempted=...)
3222+
"""
3223+
exc = ValueError('Text')
3224+
exc.add_note(note)
3225+
raise exc
3226+
3227+
3228+
def test_exception_with_multiple_notes():
3229+
"""
3230+
>>> test_exception_with_multiple_notes()
3231+
Traceback (most recent call last):
3232+
...
3233+
ValueError: Text
3234+
One
3235+
Two
3236+
"""
3237+
exc = ValueError('Text')
3238+
exc.add_note('One')
3239+
exc.add_note('Two')
3240+
raise exc
3241+
3242+
3243+
def test_syntax_error_with_note(cls, multiline=False):
3244+
"""
3245+
>>> test_syntax_error_with_note(SyntaxError)
3246+
Traceback (most recent call last):
3247+
...
3248+
SyntaxError: error
3249+
Note
3250+
3251+
>>> test_syntax_error_with_note(SyntaxError)
3252+
Traceback (most recent call last):
3253+
SyntaxError: error
3254+
Note
3255+
3256+
>>> test_syntax_error_with_note(SyntaxError)
3257+
Traceback (most recent call last):
3258+
...
3259+
File "x.py", line 23
3260+
bad syntax
3261+
SyntaxError: error
3262+
Note
3263+
3264+
>>> test_syntax_error_with_note(IndentationError)
3265+
Traceback (most recent call last):
3266+
...
3267+
IndentationError: error
3268+
Note
3269+
3270+
>>> test_syntax_error_with_note(TabError, multiline=True)
3271+
Traceback (most recent call last):
3272+
...
3273+
TabError: error
3274+
Note
3275+
Line
3276+
"""
3277+
exc = cls("error", ("x.py", 23, None, "bad syntax"))
3278+
exc.add_note('Note\nLine' if multiline else 'Note')
3279+
raise exc
3280+
3281+
3282+
def test_syntax_error_with_incorrect_expected_note():
3283+
"""
3284+
>>> def f(x):
3285+
... r'''
3286+
... >>> exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
3287+
... >>> exc.add_note('note1')
3288+
... >>> exc.add_note('note2')
3289+
... >>> raise exc
3290+
... Traceback (most recent call last):
3291+
... SyntaxError: error
3292+
... wrong note
3293+
... '''
3294+
>>> test = doctest.DocTestFinder().find(f)[0]
3295+
>>> doctest.DocTestRunner(verbose=False).run(test)
3296+
... # doctest: +ELLIPSIS
3297+
**********************************************************************
3298+
File "...", line 6, in f
3299+
Failed example:
3300+
raise exc
3301+
Expected:
3302+
Traceback (most recent call last):
3303+
SyntaxError: error
3304+
wrong note
3305+
Got:
3306+
Traceback (most recent call last):
3307+
...
3308+
SyntaxError: error
3309+
note1
3310+
note2
3311+
TestResults(failed=1, attempted=...)
3312+
"""
3313+
3314+
31713315
def load_tests(loader, tests, pattern):
31723316
tests.addTest(doctest.DocTestSuite(doctest))
31733317
tests.addTest(doctest.DocTestSuite())
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :mod:`doctest` output comparison for exceptions with notes.

0 commit comments

Comments
 (0)