Repository navigation
Doctest incorrectly locates a decorated function #115392
Description
Bug report
Bug description:
TL;DR: If a function is decorated, the doctest is unable to find the correct location of the function.
Example
Consider two simple files, main.py and decorate.py.
Contents of main.py:
from decorate import decorator
@decorator
def foo():
"""
>>> foo()
2
"""
return 42Contents of decorate.py:
import functools
def decorator(f):
@functools.wraps(f)
def inner():
return f()
return innerIf we run a doctest like so: python3 -m doctest main.py, we find the error correctly on the line number 7, the line which says >>> foo(). Traceback is output as follows.
**********************************************************************
File "/codemill/chaudhat/learning/demo/main.py", line 7, in main.foo
Failed example:
foo()
Expected:
2
Got:
42
**********************************************************************
1 items had failures:
1 of 2 in main.foo
***Test Failed*** 1 failures.
Incorrect Output
However, if we move the decorator definition in the decorate.py file by a few lines, as shown, (the space between could be empty/defining a function, etc.), we see that the doctest is unable to find the location of the decorated function, foo, and just outputs ? as the line number.
import functools
def decorator(f):
@functools.wraps(f)
def inner():
return f()
return innerTraceback:
**********************************************************************
File "/codemill/chaudhat/learning/demo/main.py", line ?, in main.foo
Failed example:
foo()
Expected:
2
Got:
42
**********************************************************************
1 items had failures:
1 of 1 in main.foo
***Test Failed*** 1 failures.
PS: If move the decorator definition by even a line up, it shows that the line, >>> foo() incorrectly lives on line 10 and not line 7.
Why?
The "?" is printed simply because while doctest is able to find the example's lineno, it is unable to understand the test's lineno. I found this after printing out the line numbers in the _failure_header function in doctest.py.
CPython versions tested on:
3.11
Operating systems tested on:
Linux
Linked PRs
- gh-115392: Fix doctest reporting incorrect line numbers for decorated functions #115440
- [3.11] gh-115392: Fix doctest reporting incorrect line numbers for decorated functions (GH-115440) #115458
- [3.12] gh-115392: Fix doctest reporting incorrect line numbers for decorated functions (GH-115440) #115459
Activity
brianschubert commentedon Feb 13, 2024
I traced this bug down to inside
DocTestFinder._find_lineno:cpython/Lib/doctest.py
Lines 1141 to 1163 in 4deb705
While processing the decorated function
foo, the following occursobjstarts asmain.foo, which thanks to the decorator is actuallyinner.obj = obj.__code__assigns the code object forinnertoobj.lineno = obj.co_firstlineno - 1retrieves the line number ofinnerinsidedecorate.pymain.pyfor the start of the next docstring afterlineno. This is a problem, since we're searching the lines ofmain.pyusing a line number fromdecorate.py.This is actually purely accidental. It only works because in this case because
inneris defined on line 6 ofdecorate.pywhile the docstring offoostarts on line 6 ofmain.py. If you moveinnerdown a few lines so that it starts on line8ofdecorate.py, you will get the line number to be10instead, since_find_linenowill find the next quote to be the closing quote offoo's docstring.boshea93 commentedon Feb 13, 2024
@brianschubert Would the fix for this be to check if objects that are functions or methods are wrapped? There could be a check if the function has the attribute
__wrapped__, or is a deeper fix needed?brianschubert commentedon Feb 13, 2024
@boshea93 I was just writing the same. I think a possible fix would be to check for a
__wrapped__attribute onobjinside theif inspect.isfunction(obj)check. We would need to do this repeatedly, since there could be multiple layers of decorators. This seem to work well enough in the tests I've run, but I'm still looking for other edge cases. PR forthcoming.gh-115392: Fix doctest reporting incorrect line numbers for decorated…
pythongh-115392: Fix doctest reporting incorrect line numbers for dec…
pythongh-115392: Fix doctest reporting incorrect line numbers for dec…
[3.11] gh-115392: Fix doctest reporting incorrect line numbers for de…
[3.12] gh-115392: Fix doctest reporting incorrect line numbers for de…
AlexWaygood commentedon Feb 14, 2024
Thanks for the fix @brianschubert, and thanks for the report @chaudhary1337!