-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Doctest report format option (#1749) #1754
Changes from 4 commits
625b603
fd8e019
87ca4b9
014ebc9
922a295
1a33025
ec7695e
e229a27
f8f690d
d5a70ac
51fa244
94731fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
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 | ||
|
||
|
||
|
@@ -17,6 +17,11 @@ def pytest_addoption(parser): | |
action="store_true", default=False, | ||
help="run doctests in all .py modules", | ||
dest="doctestmodules") | ||
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()), | ||
dest="doctestreport") | ||
group.addoption("--doctest-glob", | ||
action="append", default=[], metavar="pat", | ||
help="doctests file matching pattern, default: test*.txt", | ||
|
@@ -59,7 +64,6 @@ def toterminal(self, tw): | |
|
||
|
||
class DoctestItem(pytest.Item): | ||
|
||
def __init__(self, name, parent, runner=None, dtest=None): | ||
super(DoctestItem, self).__init__(name, parent) | ||
self.runner = runner | ||
|
@@ -94,7 +98,7 @@ def repr_failure(self, excinfo): | |
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 +295,18 @@ def _get_allow_bytes_flag(): | |
return doctest.register_optionflag('ALLOW_BYTES') | ||
|
||
|
||
def _get_report_choices(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Excellent! 👍 |
||
import doctest | ||
return dict( | ||
zip( | ||
_get_report_choices_keys(), | ||
(doctest.REPORT_UDIFF, doctest.REPORT_CDIFF, doctest.REPORT_NDIFF, doctest.REPORT_ONLY_FIRST_FAILURE, 0, ) | ||
) | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about something like: options = {
'udiff': doctest.REPORT_UDIFF,
...
}
assert set(options) == set(_get_report_choices_keys())
return options While being a slight duplication, that would make it easier to read IMHO, would make it work regardless of the order, and would fail if the keys diverge. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will try something else about this, hold on. Will opt for the roof also, will get fresher code :D There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rewrote the implementation in 51fa244 |
||
|
||
def _get_report_choices_keys(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick here: wouldn't be simpler to just have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was the first implementation, but then it required to import doctest at the option parsing time, and it was making test suite fail (because i guess some doctest dependency is importing logging, and there's a test that checks it is not the case.) See discussion at #1749 (and first impl at 625b603 that broke the tests). Let me know if you want me to change back but then I have no idea how to avoid this import related failure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see. OK that makes sense, thanks! Could you add a little explanation to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In ec7695e |
||
return ('udiff', 'cdiff', 'ndiff', 'only_first_failure', 'none', ) | ||
|
||
@pytest.fixture(scope='session') | ||
def doctest_namespace(): | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
------------- | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry realized one last thing: please add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was wondering also ... But not familiar with pytest release process. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In f8f690d |
||
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`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you generate the docs locally to make sure those links work? Sorry, I'm a very casual Sphinx user and rarely remember the proper syntax for references. 😁 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did, indeed. Never used intersphinx before so I had to find the syntax first too ^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! 👍 |
||
: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 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -475,6 +475,85 @@ def foo(): | |
"--junit-xml=junit.xml") | ||
reprec.assertoutcome(failed=1) | ||
|
||
def _run_doctest_report(self, testdir, format): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I appreciate the well written tests! I think we could move all those tests to a separate test class ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In 1a33025 |
||
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'): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this default parameter intended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh now I see what you intended in @pytest.mark.parametrize('format', ['udiff', 'UDIFF', 'uDiFf'])
def test_doctest_report_udiff(self, testdir, format): And removing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In e229a27 |
||
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest using @pytest.mark.parametrize('format', ['none', 'only_first_failure'])
def test_doctest_report_none_or_only_first_failure(self, testdir, format): There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In e229a27 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. e229a27 was about @pytest.mark.parametrize('format', ['none', 'only_first_failure'])
def test_doctest_report_none_or_only_first_failure(self, testdir):
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',
]) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad, kind of applied dumbly your comment, did not remember about this other loop. |
||
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: | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit pick: "when displaying failed doctests (#1749). Thanks
@hartym
for the PR." 😉There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean I should thank myself in the changelog? also removed the ticket reference, as it was making lint fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sounds weird but we like to have people acknowledged in the CHANGELOG. A maintainer could do it before/after merging, but then we couldn't just use GitHub's merge button so we ask people to prepare the final CHANGELOG message so we can speed things up. 😁
About the missing link, you just have to add the proper link reference... scroll down in the CHANGELOG and you will see a long list of links. Just add your link there and linting should now work.