8000 bpo-28249: fix `lineno` location for empty `DocTest` instances (GH-30… · python/cpython@8db2b3b · GitHub
[go: up one dir, main page]

Skip to content

Commit 8db2b3b

Browse files
sobolevnkumaraditya303ambv
authored
bpo-28249: fix lineno location for empty DocTest instances (GH-30498)
Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl>
1 parent 09be18a commit 8db2b3b

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

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
@@ -25,6 +25,7 @@
2525

2626
# NOTE: There are some additional tests relating to interaction with
2727
# zipimport in the test_zipimport_support test module.
28+
# There are also related tests in `test_doctest2` module.
2829

2930
######################################################################
3031
## Sample Objects (used by test cases)
@@ -460,7 +461,7 @@ def basics(): r"""
460461
>>> tests = finder.find(sample_func)
461462
462463
>>> print(tests) # doctest: +ELLIPSIS
463-
[<DocTest sample_func from test_doctest.py:33 (1 example)>]
464+
[<DocTest sample_func from test_doctest.py:34 (1 example)>]
464465
465466
The exact name depends on how test_doctest was invoked, so allow for
466467
leading path components.
@@ -642,6 +643,26 @@ def basics(): r"""
642643
1 SampleClass.double
643644
1 SampleClass.get
644645
646+
When used with `exclude_empty=False` we are also interested in line numbers
647+
of doctests that are empty.
648+
It used to be broken for quite some time until `bpo-28249`.
649+
650+
>>> from test import doctest_lineno
651+
>>> tests = doctest.DocTestFinder(exclude_empty=False).find(doctest_lineno)
652+
>>> for t in tests:
653+
... print('%5s %s' % (t.lineno, t.name))
654+
None test.doctest_lineno
655+
22 test.doctest_lineno.ClassWithDocstring
656+
30 test.doctest_lineno.ClassWithDoctest
657+
None test.doctest_lineno.ClassWithoutDocstring
658+
None test.doctest_lineno.MethodWrapper
659+
39 test.doctest_lineno.MethodWrapper.method_with_docstring
660+
45 test.doctest_lineno.MethodWrapper.method_with_doctest
661+
None test.doctest_lineno.MethodWrapper.method_without_docstring
662+
4 test.doctest_lineno.func_with_docstring
663+
12 test.doctest_lineno.func_with_doctest
664+
None test.doctest_lineno.func_without_docstring
665+
645666
Turning off Recursion
646667
~~~~~~~~~~~~~~~~~~~~~
647668
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)
0