@@ -833,6 +833,121 @@ 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+
890+ def test_lineno_fallback_multiline_matching (self ):
891+ """Test fallback to multi-line matching when no unique line exists."""
892+ # Create a module where the same lines appear multiple times,
893+ # forcing the fallback to multi-line matching
894+ module_content = '''\
895+ """Module docstring."""
896+
897+ # These lines appear in both __test__ entries
898+ # >>> x = 1
899+ # >>> x
900+ # 1
901+
902+ __test__ = {
903+ 'test_one': """
904+ >>> x = 1
905+ >>> x
906+ 1
907+ """,
908+ 'test_two': """
909+ >>> x = 1
910+ >>> x
911+ 2
912+ """,
913+ }
914+ '''
915+ with tempfile .TemporaryDirectory () as tmpdir :
916+ module_path = os .path .join (tmpdir , 'test_module_fallback.py' )
917+ with open (module_path , 'w' ) as f :
918+ f .write (module_content )
919+
920+ sys .path .insert (0 , tmpdir )
921+ try :
922+ import test_module_fallback
923+
924+ finder = doctest .DocTestFinder ()
925+ tests = finder .find (test_module_fallback )
926+
927+ # Find both __test__ entries
928+ test_one = None
929+ test_two = None
930+ for test in tests :
931+ if 'test_one' in test .name :
932+ test_one = test
933+ elif 'test_two' in test .name :
934+ test_two = test
935+
936+ # Both should have valid line numbers found via fallback
937+ self .assertIsNotNone (test_one , "test_one not found" )
938+ self .assertIsNotNone (test_two , "test_two not found" )
939+ self .assertIsNotNone (test_one .lineno ,
940+ "Line number should not be None for test_one" )
941+ self .assertIsNotNone (test_two .lineno ,
942+ "Line number should not be None for test_two" )
943+ # They should have different line numbers
944+ self .assertNotEqual (test_one .lineno , test_two .lineno ,
945+ "test_one and test_two should have different line numbers" )
946+ finally :
947+ if 'test_module_fallback' in sys .modules :
948+ del sys .modules ['test_module_fallback' ]
949+ sys .path .pop (0 )
950+
836951def test_DocTestParser (): r"""
837952Unit tests for the `DocTestParser` class.
838953
@@ -2434,7 +2549,8 @@ def test_DocTestSuite_errors():
24342549 <BLANKLINE>
24352550 >>> print(result.failures[1][1]) # doctest: +ELLIPSIS
24362551 Traceback (most recent call last):
2437- File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad
2552+ File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad
2553+ >...>> 2 + 2
24382554 AssertionError: Failed example:
24392555 2 + 2
24402556 Expected:
@@ -2464,7 +2580,8 @@ def test_DocTestSuite_errors():
24642580 <BLANKLINE>
24652581 >>> print(result.errors[1][1]) # doctest: +ELLIPSIS
24662582 Traceback (most recent call last):
2467- File "...sample_doctest_errors.py", line None, in test.test_doctest.sample_doctest_errors.__test__.bad
2583+ File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad
2584+ >...>> 1/0
24682585 File "<doctest test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
24692586 1/0
24702587 ~^~
@@ -3256,15 +3373,15 @@ def test_testmod_errors(): r"""
32563373 ~^~
32573374 ZeroDivisionError: division by zero
32583375 **********************************************************************
3259- File "...sample_doctest_errors.py", line ? , in test.test_doctest.sample_doctest_errors.__test__.bad
3376+ File "...sample_doctest_errors.py", line 37 , in test.test_doctest.sample_doctest_errors.__test__.bad
32603377 Failed example:
32613378 2 + 2
32623379 Expected:
32633380 5
32643381 Got:
32653382 4
32663383 **********************************************************************
3267- File "...sample_doctest_errors.py", line ? , in test.test_doctest.sample_doctest_errors.__test__.bad
3384+ File "...sample_doctest_errors.py", line 39 , in test.test_doctest.sample_doctest_errors.__test__.bad
32683385 Failed example:
32693386 1/0
32703387 Exception raised:
0 commit comments