Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions Lib/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,19 +1085,21 @@ def _get_test(self, obj, name, module, globs, source_lines):

def _find_lineno(self, obj, source_lines):
"""
Return a line number of the given object's docstring. Note:
this method assumes that the object has a docstring.
Return a line number of the given object's docstring.

Returns `None` if the given object does not have a docstring.
"""
lineno = None
docstring = getattr(obj, '__doc__', None)

# Find the line number for modules.
if inspect.ismodule(obj):
if inspect.ismodule(obj) and docstring is not None:
lineno = 0

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

# Find the line number for functions & methods.
if inspect.ismethod(obj): obj = obj.__func__
if inspect.isfunction(obj): obj = obj.__code__
if inspect.isfunction(obj) and getattr(obj, '__doc__', None):
# We don't use `docstring` var here, because `obj` can be changed.
obj = obj.__code__
if inspect.istraceback(obj): obj = obj.tb_frame
if inspect.isframe(obj): obj = obj.f_code
if inspect.iscode(obj):
Expand Down
50 changes: 50 additions & 0 deletions Lib/test/doctest_lineno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# This module is used in `test_doctest`.
# It must not have a docstring.

def func_with_docstring():
"""Some unrelated info."""


def func_without_docstring():
pass


def func_with_doctest():
"""
This function really contains a test case.

>>> func_with_doctest.__name__
'func_with_doctest'
"""
return 3


class ClassWithDocstring:
"""Some unrelated class information."""


class ClassWithoutDocstring:
pass


class ClassWithDoctest:
"""This class really has a test case in it.

>>> ClassWithDoctest.__name__
'ClassWithDoctest'
"""


class MethodWrapper:
def method_with_docstring(self):
"""Method with a docstring."""

def method_without_docstring(self):
pass

def method_with_doctest(self):
"""
This has a doctest!
>>> MethodWrapper.method_with_doctest.__name__
'method_with_doctest'
"""
23 changes: 22 additions & 1 deletion Lib/test/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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

######################################################################
## Sample Objects (used by test cases)
Expand Down Expand Up @@ -455,7 +456,7 @@ def basics(): r"""
>>> tests = finder.find(sample_func)

>>> print(tests) # doctest: +ELLIPSIS
[<DocTest sample_func from test_doctest.py:28 (1 example)>]
[<DocTest sample_func from test_doctest.py:29 (1 example)>]

The exact name depends on how test_doctest was invoked, so allow for
leading path components.
Expand Down Expand Up @@ -637,6 +638,26 @@ def basics(): r"""
1 SampleClass.double
1 SampleClass.get

When used with `exclude_empty=False` we are also interested in line numbers
of doctests that are empty.
It used to be broken for quite some time until `bpo-28249`.

>>> from test import doctest_lineno
>>> tests = doctest.DocTestFinder(exclude_empty=False).find(doctest_lineno)
>>> for t in tests:
... print('%5s %s' % (t.lineno, t.name))
None test.doctest_lineno
22 test.doctest_lineno.ClassWithDocstring
30 test.doctest_lineno.ClassWithDoctest
None test.doctest_lineno.ClassWithoutDocstring
None test.doctest_lineno.MethodWrapper
39 test.doctest_lineno.MethodWrapper.method_with_docstring
45 test.doctest_lineno.MethodWrapper.method_with_doctest
None test.doctest_lineno.MethodWrapper.method_without_docstring
4 test.doctest_lineno.func_with_docstring
12 test.doctest_lineno.func_with_doctest
None test.doctest_lineno.func_without_docstring

Turning off Recursion
~~~~~~~~~~~~~~~~~~~~~
DocTestFinder can be told not to look for tests in contained objects
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Set :attr:`doctest.DocTest.lineno` to ``None`` when object does not have
:attr:`__doc__`.