Skip to content

Commit d4fb6ac

Browse files
committed
Fix doctest collection of functools.cached_property objects.
1 parent 15fadd8 commit d4fb6ac

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ Tor Colvin
379379
Trevor Bekolay
380380
Tushar Sadhwani
381381
Tyler Goodlet
382+
Tyler Smart
382383
Tzu-ping Chung
383384
Vasily Kuznetsov
384385
Victor Maryama

changelog/11237.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix doctest collection of `functools.cached_property` objects.

src/_pytest/doctest.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Discover and run doctests in modules and test files."""
22
import bdb
3+
import functools
34
import inspect
45
import os
56
import platform
@@ -536,6 +537,21 @@ def _find(
536537
tests, obj, name, module, source_lines, globs, seen
537538
)
538539

540+
class CachedPropertyAwareDocTestFinder(MockAwareDocTestFinder):
541+
def _from_module(self, module, object):
542+
"""Doctest code does not take into account `@cached_property`,
543+
this is a hackish way to fix it. https://github.com/python/cpython/issues/107995
544+
545+
Wrap Doctest finder so that when it calls `_from_module` for
546+
a cached_property it uses the underlying function instead of the
547+
wrapped cached_property object.
548+
"""
549+
if isinstance(object, functools.cached_property):
550+
object = object.func
551+
552+
# Type ignored because this is a private function.
553+
return super()._from_module(module, object) # type: ignore[misc]
554+
539555
if self.path.name == "conftest.py":
540556
module = self.config.pluginmanager._importconftest(
541557
self.path,
@@ -555,7 +571,7 @@ def _find(
555571
else:
556572
raise
557573
# Uses internal doctest module parsing mechanism.
558-
finder = MockAwareDocTestFinder()
574+
finder = CachedPropertyAwareDocTestFinder()
559575
optionflags = get_optionflags(self)
560576
runner = _get_runner(
561577
verbose=False,

testing/test_doctest.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,24 @@ def test_doctestmodule(self, pytester: Pytester):
482482
reprec = pytester.inline_run(p, "--doctest-modules")
483483
reprec.assertoutcome(failed=1)
484484

485+
def test_doctest_cached_property(self, pytester: Pytester):
486+
p = pytester.makepyfile(
487+
"""
488+
import functools
489+
490+
class Foo:
491+
@functools.cached_property
492+
def foo(self):
493+
'''
494+
>>> assert False, "Tacos!"
495+
'''
496+
...
497+
"""
498+
)
499+
result = pytester.runpytest(p, "--doctest-modules")
500+
result.assert_outcomes(failed=1)
501+
assert "Tacos!" in result.stdout.str()
502+
485503
def test_doctestmodule_external_and_issue116(self, pytester: Pytester):
486504
p = pytester.mkpydir("hello")
487505
p.joinpath("__init__.py").write_text(

0 commit comments

Comments
 (0)