Skip to content

Commit 266997c

Browse files
committed
allow to ignore line breaks in doctest output
1 parent 96b7a2e commit 266997c

File tree

5 files changed

+117
-4
lines changed

5 files changed

+117
-4
lines changed

Doc/library/doctest.rst

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,8 @@ doctest decides whether actual output matches an example's expected output:
616616
sequence of whitespace within the actual output. By default, whitespace must
617617
match exactly. :const:`NORMALIZE_WHITESPACE` is especially useful when a line of
618618
expected output is very long, and you want to wrap it across multiple lines in
619-
your source.
619+
your source. If the expected output does not contain any whitespace, consider
620+
using :data:`IGNORE_LINEBREAK` or :data:`ELLIPSIS`.
620621

621622

622623
.. index:: single: ...; in doctests
@@ -666,6 +667,31 @@ doctest decides whether actual output matches an example's expected output:
666667
to the module containing the exception under test.
667668

668669

670+
.. data:: IGNORE_LINEBREAK
671+
672+
When specified, single line breaks in the expected output are eliminated,
673+
thereby allowing strings without whitespaces to span multiple lines.
674+
675+
.. doctest::
676+
:no-trim-doctest-flags:
677+
678+
>>> "foobar123456" # doctest: +IGNORE_LINEBREAK
679+
'foobar
680+
123456'
681+
682+
Consider using :data:`NORMALIZE_WHITESPACE` when strings with whitespaces
683+
need to be split across multiple lines:
684+
685+
.. doctest::
686+
:no-trim-doctest-flags:
687+
688+
>>> "the string to split" # doctest: +NORMALIZE_WHITESPACE
689+
'the string
690+
to split'
691+
692+
.. versionadded:: next
693+
694+
669695
.. data:: SKIP
670696

671697
When specified, do not run the example at all. This can be useful in contexts

Doc/whatsnew/3.15.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,23 @@ difflib
311311
(Contributed by Jiahao Li in :gh:`134580`.)
312312

313313

314+
doctest
315+
-------
316+
317+
* Add :data:`~doctest.IGNORE_LINEBREAK` option to allow breaking expected
318+
output strings without whitespaces into multiple lines:
319+
320+
.. doctest::
321+
:no-trim-doctest-flags:
322+
323+
>>> import string
324+
>>> print(string.ascii_letters) # doctest: +IGNORE_LINEBREAK
325+
abcdefghijklmnopqrstuvwxyz
326+
ABCDEFGHIJKLMNOPQRSTUVWXYZ
327+
328+
(Contributed by Bénédikt Tran in :gh:`138135`.)
329+
330+
314331
hashlib
315332
-------
316333

Lib/doctest.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def _test():
5353
'DONT_ACCEPT_TRUE_FOR_1',
5454
'DONT_ACCEPT_BLANKLINE',
5555
'NORMALIZE_WHITESPACE',
56+
'IGNORE_LINEBREAK',
5657
'ELLIPSIS',
5758
'SKIP',
5859
'IGNORE_EXCEPTION_DETAIL',
@@ -156,6 +157,7 @@ def register_optionflag(name):
156157
DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1')
157158
DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE')
158159
NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE')
160+
IGNORE_LINEBREAK = register_optionflag('IGNORE_LINEBREAK')
159161
ELLIPSIS = register_optionflag('ELLIPSIS')
160162
SKIP = register_optionflag('SKIP')
161163
IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL')
@@ -1751,9 +1753,18 @@ def check_output(self, want, got, optionflags):
17511753
if got == want:
17521754
return True
17531755

1756+
# This flag causes doctest to ignore '\n' in `want`.
1757+
# Note that this can be used in conjunction with
1758+
# the NORMALIZE_WHITESPACE and ELLIPSIS flags.
1759+
if optionflags & IGNORE_LINEBREAK:
1760+
# `want` originally ends with '\n' so we add it back
1761+
want = ''.join(want.split('\n')) + '\n'
1762+
if got == want:
1763+
return True
1764+
17541765
# This flag causes doctest to ignore any differences in the
17551766
# contents of whitespace strings. Note that this can be used
1756-
# in conjunction with the ELLIPSIS flag.
1767+
# in conjunction with the IGNORE_LINEBREAK and ELLIPSIS flags.
17571768
if optionflags & NORMALIZE_WHITESPACE:
17581769
got = ' '.join(got.split())
17591770
want = ' '.join(want.split())
@@ -2268,7 +2279,7 @@ def set_unittest_reportflags(flags):
22682279
>>> doctest.set_unittest_reportflags(ELLIPSIS)
22692280
Traceback (most recent call last):
22702281
...
2271-
ValueError: ('Only reporting flags allowed', 8)
2282+
ValueError: ('Only reporting flags allowed', 16)
22722283
22732284
>>> doctest.set_unittest_reportflags(old) == (REPORT_NDIFF |
22742285
... REPORT_ONLY_FIRST_FAILURE)
@@ -2924,6 +2935,13 @@ def get(self):
29242935
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
29252936
27, 28, 29]
29262937
""",
2938+
2939+
"line break elimination": r"""
2940+
>>> "foobar" # doctest: +IGNORE_LINEBREAK
2941+
'foo
2942+
bar
2943+
'
2944+
""",
29272945
}
29282946

29292947

Lib/test/test_doctest/test_doctest.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def test_Example(): r"""
204204
... options={doctest.ELLIPSIS: True})
205205
>>> (example.source, example.want, example.exc_msg,
206206
... example.lineno, example.indent, example.options)
207-
('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {8: True})
207+
('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {16: True})
208208
209209
The constructor normalizes the `source` string to end in a newline:
210210
@@ -1396,6 +1396,55 @@ def optionflags(): r"""
13961396
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
13971397
10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
13981398
1399+
The IGNORE_LINEBREAK flag causes all sequences of newlines to be removed:
1400+
1401+
>>> def f(x):
1402+
... '\n>>> "foobar"\n\'foo\nbar\''
1403+
1404+
>>> # Without the flag:
1405+
>>> test = doctest.DocTestFinder().find(f)[0]
1406+
>>> doctest.DocTestRunner(verbose=False).run(test)
1407+
... # doctest: +ELLIPSIS
1408+
**********************************************************************
1409+
File ..., line 3, in f
1410+
Failed example:
1411+
"foobar"
1412+
Expected:
1413+
'foo
1414+
bar'
1415+
Got:
1416+
'foobar'
1417+
TestResults(failed=1, attempted=1)
1418+
1419+
>>> # With the flag:
1420+
>>> test = doctest.DocTestFinder().find(f)[0]
1421+
>>> flags = doctest.IGNORE_LINEBREAK
1422+
>>> doctest.DocTestRunner(verbose=False, optionflags=flags).run(test)
1423+
TestResults(failed=0, attempted=1)
1424+
1425+
... ignore surrounding new lines
1426+
1427+
>>> "foobar" # doctest: +IGNORE_LINEBREAK
1428+
'
1429+
foo
1430+
bar'
1431+
>>> "foobar" # doctest: +IGNORE_LINEBREAK
1432+
'foo
1433+
bar
1434+
'
1435+
>>> "foobar" # doctest: +IGNORE_LINEBREAK
1436+
'
1437+
foo
1438+
bar
1439+
'
1440+
1441+
... non-quoted output:
1442+
1443+
>>> import string
1444+
>>> print(string.ascii_letters) # doctest: +IGNORE_LINEBREAK
1445+
abcdefghijklmnopqrstuvwxyz
1446+
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1447+
13991448
The ELLIPSIS flag causes ellipsis marker ("...") in the expected
14001449
output to match any substring in the actual output:
14011450
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`doctest`: Add :data:`~doctest.IGNORE_LINEBREAK` option to allow
2+
breaking expected output strings without whitespaces into multiple lines.
3+
Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)