diff --git a/AUTHORS b/AUTHORS index 1f842780f..2cdf587c0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -94,6 +94,7 @@ Punyashloka Biswal Quentin Pradet Ralf Schmitt Raphael Pierzina +Romain Dorgueil Roman Bolshakov Ronny Pfannschmidt Ross Lawley diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 522d63aa2..cc6d1a2df 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -70,6 +70,9 @@ time or change existing behaviors in order to make them less surprising/more use namespace in which doctests run. Thanks `@milliams`_ for the complete PR (`#1428`_). +* New ``--doctest-report`` option available to change the output format of diffs + when running (failing) doctests. Implements `#1749`_. + * New ``name`` argument to ``pytest.fixture`` decorator which allows a custom name for a fixture (to solve the funcarg-shadowing-fixture problem). Thanks `@novas0x2a`_ for the complete PR (`#1444`_). diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 3a5eb8689..acb54e35f 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -18,7 +18,9 @@ def pytest_addoption(parser): help="run doctests in all .py modules", dest="doctestmodules") group.addoption("--doctest-report", - type=str, default="UDIFF", choices=_get_report_choices().keys(), + type=str.lower, default="udiff", + help="choose another output format for diffs on doctest failure", + choices=sorted(_get_report_choices().keys()), dest="doctestreport") group.addoption("--doctest-glob", action="append", default=[], metavar="pat", @@ -62,7 +64,6 @@ class ReprFailDoctest(TerminalRepr): class DoctestItem(pytest.Item): - def __init__(self, name, parent, runner=None, dtest=None): super(DoctestItem, self).__init__(name, parent) self.runner = runner @@ -297,11 +298,11 @@ def _get_allow_bytes_flag(): def _get_report_choices(): import doctest return dict( - UDIFF=doctest.REPORT_UDIFF, - CDIFF=doctest.REPORT_CDIFF, - NDIFF=doctest.REPORT_NDIFF, - ONLY_FIRST_FAILURE=doctest.REPORT_ONLY_FIRST_FAILURE, - NONE=0, + udiff=doctest.REPORT_UDIFF, + cdiff=doctest.REPORT_CDIFF, + ndiff=doctest.REPORT_NDIFF, + only_first_failure=doctest.REPORT_ONLY_FIRST_FAILURE, + none=0, ) diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index b9b19cb85..d8df09cb4 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -16,7 +16,6 @@ from docstrings in all python modules (including regular python test modules):: pytest --doctest-modules - You can make these changes permanent in your project by putting them into a pytest.ini file like this: @@ -102,6 +101,7 @@ itself:: >>> get_unicode_greeting() # doctest: +ALLOW_UNICODE 'Hello' + The 'doctest_namespace' fixture ------------------------------- @@ -130,3 +130,20 @@ which can then be used in your doctests directly:: 10 """ pass + + +Output format +------------- + +You can change the diff output format on failure for your doctests +by using one of standard doctest modules format in options +(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`, +:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`):: + + pytest --doctest-modules --doctest-report none + pytest --doctest-modules --doctest-report udiff + pytest --doctest-modules --doctest-report cdiff + pytest --doctest-modules --doctest-report ndiff + pytest --doctest-modules --doctest-report only_first_failure + + diff --git a/testing/test_doctest.py b/testing/test_doctest.py index d0bcb1425..cfb5c22ec 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -475,6 +475,85 @@ class TestDoctests: "--junit-xml=junit.xml") reprec.assertoutcome(failed=1) + def _run_doctest_report(self, testdir, format): + testdir.makepyfile(""" + def foo(): + ''' + >>> foo() + a b + 0 1 4 + 1 2 4 + 2 3 6 + ''' + print(' a b\\n' + '0 1 4\\n' + '1 2 5\\n' + '2 3 6') + """) + return testdir.runpytest("--doctest-modules", "--doctest-report", format) + + def test_doctest_report_udiff(self, testdir, format='udiff'): + result = self._run_doctest_report(testdir, format) + result.stdout.fnmatch_lines([ + ' 0 1 4', + ' -1 2 4', + ' +1 2 5', + ' 2 3 6', + ]) + + def test_doctest_report_cdiff(self, testdir): + result = self._run_doctest_report(testdir, 'cdiff') + result.stdout.fnmatch_lines([ + ' a b', + ' 0 1 4', + ' ! 1 2 4', + ' 2 3 6', + ' --- 1,4 ----', + ' a b', + ' 0 1 4', + ' ! 1 2 5', + ' 2 3 6', + ]) + + def test_doctest_report_ndiff(self, testdir): + result = self._run_doctest_report(testdir, 'ndiff') + result.stdout.fnmatch_lines([ + ' a b', + ' 0 1 4', + ' - 1 2 4', + ' ? ^', + ' + 1 2 5', + ' ? ^', + ' 2 3 6', + ]) + + def test_doctest_report_none_or_only_first_failure(self, testdir): + for format in 'none', 'only_first_failure': + result = self._run_doctest_report(testdir, format) + result.stdout.fnmatch_lines([ + 'Expected:', + ' a b', + ' 0 1 4', + ' 1 2 4', + ' 2 3 6', + 'Got:', + ' a b', + ' 0 1 4', + ' 1 2 5', + ' 2 3 6', + ]) + + def test_doctest_report_case_insensitive(self, testdir): + for format in 'udiff', 'UDIFF', 'uDiFf': + self.test_doctest_report_udiff(testdir, format) + + def test_doctest_report_invalid(self, testdir): + result = self._run_doctest_report(testdir, 'obviously_invalid_format') + result.stderr.fnmatch_lines([ + "*error: argument --doctest-report: invalid choice: 'obviously_invalid_format' (choose from*" + ]) + + class TestLiterals: