Skip to content

Commit 6e49a74

Browse files
[7.4.x] Fix doctest collection of functools.cached_property objects. (#11403)
Co-authored-by: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
1 parent 79c2012 commit 6e49a74

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ Tony Narlock
374374
Tor Colvin
375375
Trevor Bekolay
376376
Tyler Goodlet
377+
Tyler Smart
377378
Tzu-ping Chung
378379
Vasily Kuznetsov
379380
Victor Maryama

changelog/11237.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix doctest collection of `functools.cached_property` objects.

src/_pytest/doctest.py

+20
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,25 @@ def _find(
536537
tests, obj, name, module, source_lines, globs, seen
537538
)
538539

540+
if sys.version_info < (3, 13):
541+
542+
def _from_module(self, module, object):
543+
"""`cached_property` objects are never considered a part
544+
of the 'current module'. As such they are skipped by doctest.
545+
Here we override `_from_module` to check the underlying
546+
function instead. https://github.com/python/cpython/issues/107995
547+
"""
548+
if hasattr(functools, "cached_property") and isinstance(
549+
object, functools.cached_property
550+
):
551+
object = object.func
552+
553+
# Type ignored because this is a private function.
554+
return super()._from_module(module, object) # type: ignore[misc]
555+
556+
else: # pragma: no cover
557+
pass
558+
539559
if self.path.name == "conftest.py":
540560
module = self.config.pluginmanager._importconftest(
541561
self.path,

testing/test_doctest.py

+21
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,27 @@ def test_doctestmodule(self, pytester: Pytester):
482482
reprec = pytester.inline_run(p, "--doctest-modules")
483483
reprec.assertoutcome(failed=1)
484484

485+
@pytest.mark.skipif(
486+
sys.version_info[:2] <= (3, 7), reason="Only Python 3.7 or less"
487+
)
488+
def test_doctest_cached_property(self, pytester: Pytester):
489+
p = pytester.makepyfile(
490+
"""
491+
import functools
492+
493+
class Foo:
494+
@functools.cached_property
495+
def foo(self):
496+
'''
497+
>>> assert False, "Tacos!"
498+
'''
499+
...
500+
"""
501+
)
502+
result = pytester.runpytest(p, "--doctest-modules")
503+
result.assert_outcomes(failed=1)
504+
assert "Tacos!" in result.stdout.str()
505+
485506
def test_doctestmodule_external_and_issue116(self, pytester: Pytester):
486507
p = pytester.mkpydir("hello")
487508
p.joinpath("__init__.py").write_text(

0 commit comments

Comments
 (0)