Skip to content

Commit c146525

Browse files
ambvkumaraditya303sobolevn
authored
[3.10] bpo-28249: fix lineno location for empty DocTest instances (GH-30498) (#92981)
(cherry picked from commit 8db2b3b) Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
1 parent 5d7f3dc commit c146525

4 files changed

Lines changed: 83 additions & 6 deletions

File tree

Lib/doctest.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,19 +1085,21 @@ def _get_test(self, obj, name, module, globs, source_lines):
10851085

10861086
def _find_lineno(self, obj, source_lines):
10871087
"""
1088-
Return a line number of the given object's docstring. Note:
1089-
this method assumes that the object has a docstring.
1088+
Return a line number of the given object's docstring.
1089+
1090+
Returns `None` if the given object does not have a docstring.
10901091
"""
10911092
lineno = None
1093+
docstring = getattr(obj, '__doc__', None)
10921094

10931095
# Find the line number for modules.
1094-
if inspect.ismodule(obj):
1096+
if inspect.ismodule(obj) and docstring is not None:
10951097
lineno = 0
10961098

10971099
# Find the line number for classes.
10981100
# Note: this could be fooled if a class is defined multiple
10991101
# times in a single file.
1100-
if inspect.isclass(obj):
1102+
if inspect.isclass(obj) and docstring is not None:
11011103
if source_lines is None:
11021104
return None
11031105
pat = re.compile(r'^\s*class\s*%s\b' %
@@ -1109,7 +1111,9 @@ def _find_lineno(self, obj, source_lines):
11091111

11101112
# Find the line number for functions & methods.
11111113
if inspect.ismethod(obj): obj = obj.__func__
1112-
if inspect.isfunction(obj): obj = obj.__code__
1114+
if inspect.isfunction(obj) and getattr(obj, '__doc__', None):
1115+
# We don't use `docstring` var here, because `obj` can be changed.
1116+
obj = obj.__code__
11131117
if inspect.istraceback(obj): obj = obj.tb_frame
11141118
if inspect.isframe(obj): obj = obj.f_code
11151119
if inspect.iscode(obj):

Lib/test/doctest_lineno.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This module is used in `test_doctest`.
2+
# It must not have a docstring.
3+
4+
def func_with_docstring():
5+
"""Some unrelated info."""
6+
7+
8+
def func_without_docstring():
9+
pass
10+
11+
12+
def func_with_doctest():
13+
"""
14+
This function really contains a test case.
15+
16+
>>> func_with_doctest.__name__
17+
'func_with_doctest'
18+
"""
19+
return 3
20+
21+
22+
class ClassWithDocstring:
23+
"""Some unrelated class information."""
24+
25+
26+
class ClassWithoutDocstring:
27+
pass
28+
29+
30+
class ClassWithDoctest:
31+
"""This class really has a test case in it.
32+
33+
>>> ClassWithDoctest.__name__
34+
'ClassWithDoctest'
35+
"""
36+
37+
38+
class MethodWrapper:
39+
def method_with_docstring(self):
40+
"""Method with a docstring."""
41+
42+
def method_without_docstring(self):
43+
pass
44+
45+
def method_with_doctest(self):
46+
"""
47+
This has a doctest!
48+
>>> MethodWrapper.method_with_doctest.__name__
49+
'method_with_doctest'
50+
"""

Lib/test/test_doctest.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
# NOTE: There are some additional tests relating to interaction with
2222
# zipimport in the test_zipimport_support test module.
23+
# There are also related tests in `test_doctest2` module.
2324

2425
######################################################################
2526
## Sample Objects (used by test cases)
@@ -455,7 +456,7 @@ def basics(): r"""
455456
>>> tests = finder.find(sample_func)
456457
457458
>>> print(tests) # doctest: +ELLIPSIS
458-
[<DocTest sample_func from test_doctest.py:28 (1 example)>]
459+
[<DocTest sample_func from test_doctest.py:29 (1 example)>]
459460
460461
The exact name depends on how test_doctest was invoked, so allow for
461462
leading path components.
@@ -637,6 +638,26 @@ def basics(): r"""
637638
1 SampleClass.double
638639
1 SampleClass.get
639640
641+
When used with `exclude_empty=False` we are also interested in line numbers
642+
of doctests that are empty.
643+
It used to be broken for quite some time until `bpo-28249`.
644+
645+
>>> from test import doctest_lineno
646+
>>> tests = doctest.DocTestFinder(exclude_empty=False).find(doctest_lineno)
647+
>>> for t in tests:
648+
... print('%5s %s' % (t.lineno, t.name))
649+
None test.doctest_lineno
650+
22 test.doctest_lineno.ClassWithDocstring
651+
30 test.doctest_lineno.ClassWithDoctest
652+
None test.doctest_lineno.ClassWithoutDocstring
653+
None test.doctest_lineno.MethodWrapper
654+
39 test.doctest_lineno.MethodWrapper.method_with_docstring
655+
45 test.doctest_lineno.MethodWrapper.method_with_doctest
656+
None test.doctest_lineno.MethodWrapper.method_without_docstring
657+
4 test.doctest_lineno.func_with_docstring
658+
12 test.doctest_lineno.func_with_doctest
659+
None test.doctest_lineno.func_without_docstring
660+
640661
Turning off Recursion
641662
~~~~~~~~~~~~~~~~~~~~~
642663
DocTestFinder can be told not to look for tests in contained objects
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Set :attr:`doctest.DocTest.lineno` to ``None`` when object does not have
2+
:attr:`__doc__`.

0 commit comments

Comments
 (0)