Skip to content

Commit 4aede6f

Browse files
committed
fixed conflicts
2 parents d000f25 + 9891593 commit 4aede6f

File tree

5 files changed

+141
-5
lines changed

5 files changed

+141
-5
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ Punyashloka Biswal
9595
Quentin Pradet
9696
Ralf Schmitt
9797
Raphael Pierzina
98+
Romain Dorgueil
9899
Roman Bolshakov
99100
Ronny Pfannschmidt
100101
Ross Lawley

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ time or change existing behaviors in order to make them less surprising/more use
7070
namespace in which doctests run.
7171
Thanks `@milliams`_ for the complete PR (`#1428`_).
7272

73+
* New ``--doctest-report`` option available to change the output format of diffs
74+
when running (failing) doctests (implements `#1749`_).
75+
Thanks `@hartym`_ for the PR.
76+
7377
* New ``name`` argument to ``pytest.fixture`` decorator which allows a custom name
7478
for a fixture (to solve the funcarg-shadowing-fixture problem).
7579
Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
@@ -317,6 +321,7 @@ time or change existing behaviors in order to make them less surprising/more use
317321
.. _#1684: https://github.com/pytest-dev/pytest/pull/1684
318322
.. _#1723: https://github.com/pytest-dev/pytest/pull/1723
319323
.. _#1539: https://github.com/pytest-dev/pytest/issues/1539
324+
.. _#1749: https://github.com/pytest-dev/pytest/issues/1749
320325

321326
.. _@DRMacIver: https://github.com/DRMacIver
322327
.. _@RedBeardCode: https://github.com/RedBeardCode
@@ -331,6 +336,7 @@ time or change existing behaviors in order to make them less surprising/more use
331336
.. _@fengxx: https://github.com/fengxx
332337
.. _@flub: https://github.com/flub
333338
.. _@graingert: https://github.com/graingert
339+
.. _@hartym: https://github.com/hartym
334340
.. _@kalekundert: https://github.com/kalekundert
335341
.. _@kvas-it: https://github.com/kvas-it
336342
.. _@marscher: https://github.com/marscher

_pytest/doctest.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@
44
import traceback
55

66
import pytest
7-
from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo
7+
from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr
88
from _pytest.fixtures import FixtureRequest
99

1010

11+
DOCTEST_REPORT_CHOICE_NONE = 'none'
12+
DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff'
13+
DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff'
14+
DOCTEST_REPORT_CHOICE_UDIFF = 'udiff'
15+
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure'
16+
17+
DOCTEST_REPORT_CHOICES = (
18+
DOCTEST_REPORT_CHOICE_NONE,
19+
DOCTEST_REPORT_CHOICE_CDIFF,
20+
DOCTEST_REPORT_CHOICE_NDIFF,
21+
DOCTEST_REPORT_CHOICE_UDIFF,
22+
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
23+
)
1124

1225
def pytest_addoption(parser):
1326
parser.addini('doctest_optionflags', 'option flags for doctests',
@@ -17,6 +30,11 @@ def pytest_addoption(parser):
1730
action="store_true", default=False,
1831
help="run doctests in all .py modules",
1932
dest="doctestmodules")
33+
group.addoption("--doctest-report",
34+
type=str.lower, default="udiff",
35+
help="choose another output format for diffs on doctest failure",
36+
choices=DOCTEST_REPORT_CHOICES,
37+
dest="doctestreport")
2038
group.addoption("--doctest-glob",
2139
action="append", default=[], metavar="pat",
2240
help="doctests file matching pattern, default: test*.txt",
@@ -59,7 +77,6 @@ def toterminal(self, tw):
5977

6078

6179
class DoctestItem(pytest.Item):
62-
6380
def __init__(self, name, parent, runner=None, dtest=None):
6481
super(DoctestItem, self).__init__(name, parent)
6582
self.runner = runner
@@ -94,7 +111,7 @@ def repr_failure(self, excinfo):
94111
message = excinfo.type.__name__
95112
reprlocation = ReprFileLocation(filename, lineno, message)
96113
checker = _get_checker()
97-
REPORT_UDIFF = doctest.REPORT_UDIFF
114+
report_choice = _get_report_choice(self.config.getoption("doctestreport"))
98115
if lineno is not None:
99116
lines = doctestfailure.test.docstring.splitlines(False)
100117
# add line numbers to the left of the error message
@@ -110,7 +127,7 @@ def repr_failure(self, excinfo):
110127
indent = '...'
111128
if excinfo.errisinstance(doctest.DocTestFailure):
112129
lines += checker.output_difference(example,
113-
doctestfailure.got, REPORT_UDIFF).split("\n")
130+
doctestfailure.got, report_choice).split("\n")
114131
else:
115132
inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
116133
lines += ["UNEXPECTED EXCEPTION: %s" %
@@ -291,6 +308,21 @@ def _get_allow_bytes_flag():
291308
return doctest.register_optionflag('ALLOW_BYTES')
292309

293310

311+
def _get_report_choice(key):
312+
"""
313+
This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
314+
importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
315+
"""
316+
import doctest
317+
318+
return {
319+
DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
320+
DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
321+
DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
322+
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
323+
DOCTEST_REPORT_CHOICE_NONE: 0,
324+
}[key]
325+
294326
@pytest.fixture(scope='session')
295327
def doctest_namespace():
296328
"""

doc/en/doctest.rst

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ from docstrings in all python modules (including regular
1616
python test modules)::
1717

1818
pytest --doctest-modules
19-
2019
You can make these changes permanent in your project by
2120
putting them into a pytest.ini file like this:
2221

@@ -102,6 +101,7 @@ itself::
102101
>>> get_unicode_greeting() # doctest: +ALLOW_UNICODE
103102
'Hello'
104103

104+
105105
The 'doctest_namespace' fixture
106106
-------------------------------
107107

@@ -130,3 +130,22 @@ which can then be used in your doctests directly::
130130
10
131131
"""
132132
pass
133+
134+
135+
Output format
136+
-------------
137+
138+
.. versionadded:: 3.0
139+
140+
You can change the diff output format on failure for your doctests
141+
by using one of standard doctest modules format in options
142+
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
143+
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`)::
144+
145+
pytest --doctest-modules --doctest-report none
146+
pytest --doctest-modules --doctest-report udiff
147+
pytest --doctest-modules --doctest-report cdiff
148+
pytest --doctest-modules --doctest-report ndiff
149+
pytest --doctest-modules --doctest-report only_first_failure
150+
151+

testing/test_doctest.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,3 +784,81 @@ def foo():
784784
""")
785785
reprec = testdir.inline_run(p, "--doctest-modules")
786786
reprec.assertoutcome(passed=1)
787+
788+
789+
class TestDoctestReportingOption:
790+
def _run_doctest_report(self, testdir, format):
791+
testdir.makepyfile("""
792+
def foo():
793+
'''
794+
>>> foo()
795+
a b
796+
0 1 4
797+
1 2 4
798+
2 3 6
799+
'''
800+
print(' a b\\n'
801+
'0 1 4\\n'
802+
'1 2 5\\n'
803+
'2 3 6')
804+
""")
805+
return testdir.runpytest("--doctest-modules", "--doctest-report", format)
806+
807+
@pytest.mark.parametrize('format', ['udiff', 'UDIFF', 'uDiFf'])
808+
def test_doctest_report_udiff(self, testdir, format):
809+
result = self._run_doctest_report(testdir, format)
810+
result.stdout.fnmatch_lines([
811+
' 0 1 4',
812+
' -1 2 4',
813+
' +1 2 5',
814+
' 2 3 6',
815+
])
816+
817+
def test_doctest_report_cdiff(self, testdir):
818+
result = self._run_doctest_report(testdir, 'cdiff')
819+
result.stdout.fnmatch_lines([
820+
' a b',
821+
' 0 1 4',
822+
' ! 1 2 4',
823+
' 2 3 6',
824+
' --- 1,4 ----',
825+
' a b',
826+
' 0 1 4',
827+
' ! 1 2 5',
828+
' 2 3 6',
829+
])
830+
831+
def test_doctest_report_ndiff(self, testdir):
832+
result = self._run_doctest_report(testdir, 'ndiff')
833+
result.stdout.fnmatch_lines([
834+
' a b',
835+
' 0 1 4',
836+
' - 1 2 4',
837+
' ? ^',
838+
' + 1 2 5',
839+
' ? ^',
840+
' 2 3 6',
841+
])
842+
843+
@pytest.mark.parametrize('format', ['none', 'only_first_failure'])
844+
def test_doctest_report_none_or_only_first_failure(self, testdir, format):
845+
result = self._run_doctest_report(testdir, format)
846+
result.stdout.fnmatch_lines([
847+
'Expected:',
848+
' a b',
849+
' 0 1 4',
850+
' 1 2 4',
851+
' 2 3 6',
852+
'Got:',
853+
' a b',
854+
' 0 1 4',
855+
' 1 2 5',
856+
' 2 3 6',
857+
])
858+
859+
def test_doctest_report_invalid(self, testdir):
860+
result = self._run_doctest_report(testdir, 'obviously_invalid_format')
861+
result.stderr.fnmatch_lines([
862+
"*error: argument --doctest-report: invalid choice: 'obviously_invalid_format' (choose from*"
863+
])
864+

0 commit comments

Comments
 (0)