From 625b603f1f414e59f306116ba8194673fdb0ce83 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 13:06:05 +0200 Subject: [PATCH 01/12] Implements an option to choose the doctest output format in case of failure. (fixes #1749) --- _pytest/doctest.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 649077533..3a5eb8689 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -17,6 +17,9 @@ def pytest_addoption(parser): action="store_true", default=False, help="run doctests in all .py modules", dest="doctestmodules") + group.addoption("--doctest-report", + type=str, default="UDIFF", choices=_get_report_choices().keys(), + dest="doctestreport") group.addoption("--doctest-glob", action="append", default=[], metavar="pat", help="doctests file matching pattern, default: test*.txt", @@ -94,7 +97,7 @@ class DoctestItem(pytest.Item): message = excinfo.type.__name__ reprlocation = ReprFileLocation(filename, lineno, message) checker = _get_checker() - REPORT_UDIFF = doctest.REPORT_UDIFF + REPORT_UDIFF = _get_report_choices().get(self.config.getoption("doctestreport")) if lineno is not None: lines = doctestfailure.test.docstring.splitlines(False) # add line numbers to the left of the error message @@ -291,6 +294,17 @@ def _get_allow_bytes_flag(): return doctest.register_optionflag('ALLOW_BYTES') +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, + ) + + @pytest.fixture(scope='session') def doctest_namespace(): """ From fd8e019cc12391995f05cad95c3f93bf0489f32d Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 14:40:25 +0200 Subject: [PATCH 02/12] Choose the doctest output format in case of failure, still work in progress as a few checks fail (related to #1749) --- AUTHORS | 1 + CHANGELOG.rst | 3 ++ _pytest/doctest.py | 15 ++++---- doc/en/doctest.rst | 19 +++++++++- testing/test_doctest.py | 79 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 8 deletions(-) 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: From 87ca4b95fb76b7238db7b2e2ad14505bc5802b01 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 14:50:24 +0200 Subject: [PATCH 03/12] Separate the option keys and value to avoid importing "doctest" (and tested things like "logging") for argument parsing (fixes #1749) --- _pytest/doctest.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index acb54e35f..59846a21d 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -4,7 +4,7 @@ from __future__ import absolute_import import traceback import pytest -from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo +from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr from _pytest.fixtures import FixtureRequest @@ -20,7 +20,7 @@ def pytest_addoption(parser): group.addoption("--doctest-report", type=str.lower, default="udiff", help="choose another output format for diffs on doctest failure", - choices=sorted(_get_report_choices().keys()), + choices=sorted(_get_report_choices_keys()), dest="doctestreport") group.addoption("--doctest-glob", action="append", default=[], metavar="pat", @@ -298,13 +298,14 @@ 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, + zip( + _get_report_choices_keys(), + (doctest.REPORT_UDIFF, doctest.REPORT_CDIFF, doctest.REPORT_NDIFF, doctest.REPORT_ONLY_FIRST_FAILURE, 0, ) + ) ) +def _get_report_choices_keys(): + return ('udiff', 'cdiff', 'ndiff', 'only_first_failure', 'none', ) @pytest.fixture(scope='session') def doctest_namespace(): From 014ebc9202b784e9b66cc8d61aca2faec8511198 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:04:19 +0200 Subject: [PATCH 04/12] Removed bug reference in changelog because it makes the linting fail. --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cc6d1a2df..a5fad89e1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -71,7 +71,7 @@ time or change existing behaviors in order to make them less surprising/more use 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`_. + when running (failing) doctests. * New ``name`` argument to ``pytest.fixture`` decorator which allows a custom name for a fixture (to solve the funcarg-shadowing-fixture problem). From 922a295729f53f5851b7748753ccf2ae1626ab2f Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:15:17 +0200 Subject: [PATCH 05/12] fixes changelog, I think I got it --- CHANGELOG.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a5fad89e1..5f8ba271d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -71,7 +71,8 @@ time or change existing behaviors in order to make them less surprising/more use Thanks `@milliams`_ for the complete PR (`#1428`_). * New ``--doctest-report`` option available to change the output format of diffs - when running (failing) doctests. + when running (failing) doctests (implements `#1749`_). + Thanks `@hartym`_ for the PR. * New ``name`` argument to ``pytest.fixture`` decorator which allows a custom name for a fixture (to solve the funcarg-shadowing-fixture problem). @@ -317,6 +318,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _#1664: https://github.com/pytest-dev/pytest/pull/1664 .. _#1684: https://github.com/pytest-dev/pytest/pull/1684 .. _#1723: https://github.com/pytest-dev/pytest/pull/1723 +.. _#1749: https://github.com/pytest-dev/pytest/issues/1749 .. _@DRMacIver: https://github.com/DRMacIver .. _@RedBeardCode: https://github.com/RedBeardCode @@ -331,6 +333,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _@fengxx: https://github.com/fengxx .. _@flub: https://github.com/flub .. _@graingert: https://github.com/graingert +.. _@hartym: https://github.com/hartym .. _@kalekundert: https://github.com/kalekundert .. _@kvas-it: https://github.com/kvas-it .. _@marscher: https://github.com/marscher From 1a33025a7688cd1a00fd3c3b9467574e88d5f1c8 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:16:23 +0200 Subject: [PATCH 06/12] Refactors test to be in its own class, as discussed with @nicoddemus. --- testing/test_doctest.py | 160 ++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index cfb5c22ec..8b72c122a 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -475,85 +475,6 @@ 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: @@ -863,3 +784,84 @@ class TestDoctestNamespaceFixture: """) reprec = testdir.inline_run(p, "--doctest-modules") reprec.assertoutcome(passed=1) + + +class TestDoctestReportingOption: + 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*" + ]) + From ec7695e15d26d6cf6610212b2e69d4d5fb034dd7 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:19:18 +0200 Subject: [PATCH 07/12] adds a bit of doctest hint on why the key and value getters are separate functions. --- _pytest/doctest.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 59846a21d..e4e028535 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -295,7 +295,18 @@ def _get_allow_bytes_flag(): return doctest.register_optionflag('ALLOW_BYTES') +def _get_report_choices_keys(): + """ + Returns the report choices keys. Separate function (see just below) because this is used to declare options and + we can't import the doctest module at that time. + """ + return ('udiff', 'cdiff', 'ndiff', 'only_first_failure', 'none', ) + def _get_report_choices(): + """ + See `_get_report_choices_keys` to understand why this strange implementation. Be careful with order, the values + order should be the same as the returned keys above. + """ import doctest return dict( zip( @@ -304,9 +315,6 @@ def _get_report_choices(): ) ) -def _get_report_choices_keys(): - return ('udiff', 'cdiff', 'ndiff', 'only_first_failure', 'none', ) - @pytest.fixture(scope='session') def doctest_namespace(): """ From e229a27e8b4f84a99ed6e01e82c56c50505d9965 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:21:59 +0200 Subject: [PATCH 08/12] using @pytest.mark.parametrize instead of calling one test many times from another as suggested by @nicoddemus in pr #1754 --- testing/test_doctest.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 8b72c122a..c31098483 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -804,7 +804,8 @@ class TestDoctestReportingOption: """) return testdir.runpytest("--doctest-modules", "--doctest-report", format) - def test_doctest_report_udiff(self, testdir, format='udiff'): + @pytest.mark.parametrize('format', ['udiff', 'UDIFF', 'uDiFf']) + def test_doctest_report_udiff(self, testdir, format): result = self._run_doctest_report(testdir, format) result.stdout.fnmatch_lines([ ' 0 1 4', @@ -855,10 +856,6 @@ class TestDoctestReportingOption: ' 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([ From f8f690de6417b1faa0b6e7a820fff0b5b19144c6 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:30:06 +0200 Subject: [PATCH 09/12] adds the versionadded flag in docs (#1749) --- doc/en/doctest.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index d8df09cb4..7875c25e0 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -135,6 +135,8 @@ which can then be used in your doctests directly:: Output format ------------- +.. versionadded:: 3.0 + 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`, From d5a70acd480f182fe77bf232508f3be862e516ff Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 15:58:13 +0200 Subject: [PATCH 10/12] Simplify test in test_doctest_report_none_or_only_first_failure. --- testing/test_doctest.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index c31098483..4ea2cc58e 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -840,21 +840,21 @@ class TestDoctestReportingOption: ' 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', - ]) + @pytest.mark.parametrize('format', ['none', 'only_first_failure']) + def test_doctest_report_none_or_only_first_failure(self, testdir, format): + 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_invalid(self, testdir): result = self._run_doctest_report(testdir, 'obviously_invalid_format') From 51fa2446504b219a42a53343517a29a7213fd8d4 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 16:18:12 +0200 Subject: [PATCH 11/12] Cleaner implementation of #1749. --- _pytest/doctest.py | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index e4e028535..40b97d408 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -8,6 +8,19 @@ from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr from _pytest.fixtures import FixtureRequest +DOCTEST_REPORT_CHOICE_NONE = 'none' +DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff' +DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff' +DOCTEST_REPORT_CHOICE_UDIFF = 'udiff' +DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure' + +DOCTEST_REPORT_CHOICES = ( + DOCTEST_REPORT_CHOICE_NONE, + DOCTEST_REPORT_CHOICE_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF, + DOCTEST_REPORT_CHOICE_UDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, +) def pytest_addoption(parser): parser.addini('doctest_optionflags', 'option flags for doctests', @@ -20,7 +33,7 @@ def pytest_addoption(parser): group.addoption("--doctest-report", type=str.lower, default="udiff", help="choose another output format for diffs on doctest failure", - choices=sorted(_get_report_choices_keys()), + choices=DOCTEST_REPORT_CHOICES, dest="doctestreport") group.addoption("--doctest-glob", action="append", default=[], metavar="pat", @@ -98,7 +111,7 @@ class DoctestItem(pytest.Item): message = excinfo.type.__name__ reprlocation = ReprFileLocation(filename, lineno, message) checker = _get_checker() - REPORT_UDIFF = _get_report_choices().get(self.config.getoption("doctestreport")) + REPORT_UDIFF = _get_report_choice(self.config.getoption("doctestreport")) if lineno is not None: lines = doctestfailure.test.docstring.splitlines(False) # add line numbers to the left of the error message @@ -295,25 +308,20 @@ def _get_allow_bytes_flag(): return doctest.register_optionflag('ALLOW_BYTES') -def _get_report_choices_keys(): +def _get_report_choice(key): """ - Returns the report choices keys. Separate function (see just below) because this is used to declare options and - we can't import the doctest module at that time. - """ - return ('udiff', 'cdiff', 'ndiff', 'only_first_failure', 'none', ) - -def _get_report_choices(): - """ - See `_get_report_choices_keys` to understand why this strange implementation. Be careful with order, the values - order should be the same as the returned keys above. + This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid + importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests. """ import doctest - return dict( - zip( - _get_report_choices_keys(), - (doctest.REPORT_UDIFF, doctest.REPORT_CDIFF, doctest.REPORT_NDIFF, doctest.REPORT_ONLY_FIRST_FAILURE, 0, ) - ) - ) + + return { + DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, + DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, + DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, + DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, + DOCTEST_REPORT_CHOICE_NONE: 0, + }[key] @pytest.fixture(scope='session') def doctest_namespace(): From 94731fc2a1b0f48d8b6687c95d89179b44bc4b17 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Sat, 23 Jul 2016 16:26:33 +0200 Subject: [PATCH 12/12] Changes variable name so it better describes what it does now. --- _pytest/doctest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_pytest/doctest.py b/_pytest/doctest.py index 40b97d408..144aa2a6d 100644 --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -111,7 +111,7 @@ class DoctestItem(pytest.Item): message = excinfo.type.__name__ reprlocation = ReprFileLocation(filename, lineno, message) checker = _get_checker() - REPORT_UDIFF = _get_report_choice(self.config.getoption("doctestreport")) + report_choice = _get_report_choice(self.config.getoption("doctestreport")) if lineno is not None: lines = doctestfailure.test.docstring.splitlines(False) # add line numbers to the left of the error message @@ -127,7 +127,7 @@ class DoctestItem(pytest.Item): indent = '...' if excinfo.errisinstance(doctest.DocTestFailure): lines += checker.output_difference(example, - doctestfailure.got, REPORT_UDIFF).split("\n") + doctestfailure.got, report_choice).split("\n") else: inner_excinfo = ExceptionInfo(excinfo.value.exc_info) lines += ["UNEXPECTED EXCEPTION: %s" %