Skip to content

Commit c6be833

Browse files
CuriousLearnerJurjen N.E. Bos
andcommitted
gh-69113: Fix doctest to report line numbers for __test__ strings
Enhanced the _find_lineno method in doctest to correctly identify and report line numbers for doctests defined in __test__ dictionaries when formatted as triple-quoted strings. The implementation finds unique lines in the test string and matches them in the source file to calculate the correct starting line number. Previously, doctest would report "line None" for __test__ dictionary strings, making it difficult to debug failing tests. Co-Authored-By: Jurjen N.E. Bos <jneb@users.sourceforge.net>
1 parent ed81baf commit c6be833

3 files changed

Lines changed: 70 additions & 4 deletions

File tree

Lib/doctest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,15 @@ def _find_lineno(self, obj, source_lines):
11671167
if pat.match(source_lines[lineno]):
11681168
return lineno
11691169

1170+
# Handle __test__ string doctests by finding a unique line
1171+
# in the string and matching it in the source file
1172+
if isinstance(obj, str) and source_lines is not None:
1173+
# This will find __test__ string doctests if and only if the string
1174+
# contains any unique line.
1175+
for offset, line in enumerate(obj.splitlines(keepends=True)):
1176+
if source_lines.count(line) == 1:
1177+
return source_lines.index(line) - offset
1178+
11701179
# We couldn't find the line number.
11711180
return None
11721181

Lib/test/test_doctest/test_doctest.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,60 @@ def test_empty_namespace_package(self):
833833
self.assertEqual(len(include_empty_finder.find(mod)), 1)
834834
self.assertEqual(len(exclude_empty_finder.find(mod)), 0)
835835

836+
def test_lineno_of_test_dict_strings(self):
837+
"""Test that line numbers are correctly found for __test__ dict strings."""
838+
# Create a temporary module with a __test__ dict containing triple-quoted strings
839+
module_content = '''\
840+
"""Module docstring."""
841+
842+
def dummy_function():
843+
"""Dummy function docstring."""
844+
pass
845+
846+
__test__ = {
847+
'test_string': """
848+
This is a test string.
849+
>>> 1 + 1
850+
2
851+
""",
852+
}
853+
'''
854+
with tempfile.TemporaryDirectory() as tmpdir:
855+
module_path = os.path.join(tmpdir, 'test_module_lineno.py')
856+
with open(module_path, 'w') as f:
857+
f.write(module_content)
858+
859+
sys.path.insert(0, tmpdir)
860+
try:
861+
# Import the module
862+
import test_module_lineno
863+
864+
# Use DocTestFinder to find tests
865+
finder = doctest.DocTestFinder()
866+
tests = finder.find(test_module_lineno)
867+
868+
# Find the test from __test__ dict
869+
test_dict_test = None
870+
for test in tests:
871+
if '__test__' in test.name:
872+
test_dict_test = test
873+
break
874+
875+
# Assert that we found the test and it has a valid line number
876+
self.assertIsNotNone(test_dict_test, "__test__ dict test not found")
877+
# The line number should not be None - this is what the bug fix addresses
878+
self.assertIsNotNone(test_dict_test.lineno,
879+
"Line number should not be None for __test__ dict strings")
880+
# The line number should be around line 9 (where the triple-quoted string starts)
881+
# Allowing some tolerance for exact line number
882+
self.assertGreater(test_dict_test.lineno, 0,
883+
"Line number should be positive")
884+
finally:
885+
# Clean up
886+
if 'test_module_lineno' in sys.modules:
887+
del sys.modules['test_module_lineno']
888+
sys.path.pop(0)
889+
836890
def test_DocTestParser(): r"""
837891
Unit tests for the `DocTestParser` class.
838892
@@ -2434,7 +2488,8 @@ def test_DocTestSuite_errors():
24342488
<BLANKLINE>
24352489
>>> print(result.failures[1][1]) # doctest: +ELLIPSIS
24362490
Traceback (most recent call last):
2437-
File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad
2491+
File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad
2492+
>...>> 2 + 2
24382493
AssertionError: Failed example:
24392494
2 + 2
24402495
Expected:
@@ -2464,7 +2519,8 @@ def test_DocTestSuite_errors():
24642519
<BLANKLINE>
24652520
>>> print(result.errors[1][1]) # doctest: +ELLIPSIS
24662521
Traceback (most recent call last):
2467-
File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad
2522+
File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad
2523+
>...>> 1/0
24682524
File "<doctest test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
24692525
1/0
24702526
~^~
@@ -3256,15 +3312,15 @@ def test_testmod_errors(): r"""
32563312
~^~
32573313
ZeroDivisionError: division by zero
32583314
**********************************************************************
3259-
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
3315+
File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad
32603316
Failed example:
32613317
2 + 2
32623318
Expected:
32633319
5
32643320
Got:
32653321
4
32663322
**********************************************************************
3267-
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
3323+
File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad
32683324
Failed example:
32693325
1/0
32703326
Exception raised:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :mod:`doctest` to correctly report line numbers for doctests in ``__test__`` dictionary when formatted as triple-quoted strings by finding unique lines in the string and matching them in the source file.

0 commit comments

Comments
 (0)