From 3dc06573475352cbc308c172dad5684c4d11e45b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 7 Sep 2023 14:39:02 +0200 Subject: [PATCH 1/2] [7.4.x] Fix doctest collection of `functools.cached_property` objects. --- AUTHORS | 1 + changelog/11237.bugfix.rst | 1 + src/_pytest/doctest.py | 18 ++++++++++++++++++ testing/test_doctest.py | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 changelog/11237.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 74043fcfb6c..e8456d92b31 100644 --- a/AUTHORS +++ b/AUTHORS @@ -374,6 +374,7 @@ Tony Narlock Tor Colvin Trevor Bekolay Tyler Goodlet +Tyler Smart Tzu-ping Chung Vasily Kuznetsov Victor Maryama diff --git a/changelog/11237.bugfix.rst b/changelog/11237.bugfix.rst new file mode 100644 index 00000000000..d054fc18d1c --- /dev/null +++ b/changelog/11237.bugfix.rst @@ -0,0 +1 @@ +Fix doctest collection of `functools.cached_property` objects. diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 455ad62cc9c..73ee483b382 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -1,5 +1,6 @@ """Discover and run doctests in modules and test files.""" import bdb +import functools import inspect import os import platform @@ -536,6 +537,23 @@ def _find( tests, obj, name, module, source_lines, globs, seen ) + if sys.version_info < (3, 13): + + def _from_module(self, module, object): + """`cached_property` objects are never considered a part + of the 'current module'. As such they are skipped by doctest. + Here we override `_from_module` to check the underlying + function instead. https://github.com/python/cpython/issues/107995 + """ + if isinstance(object, functools.cached_property): + object = object.func + + # Type ignored because this is a private function. + return super()._from_module(module, object) # type: ignore[misc] + + else: # pragma: no cover + pass + if self.path.name == "conftest.py": module = self.config.pluginmanager._importconftest( self.path, diff --git a/testing/test_doctest.py b/testing/test_doctest.py index dfe569987ca..2cf5b019c0a 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -482,6 +482,24 @@ def test_doctestmodule(self, pytester: Pytester): reprec = pytester.inline_run(p, "--doctest-modules") reprec.assertoutcome(failed=1) + def test_doctest_cached_property(self, pytester: Pytester): + p = pytester.makepyfile( + """ + import functools + + class Foo: + @functools.cached_property + def foo(self): + ''' + >>> assert False, "Tacos!" + ''' + ... + """ + ) + result = pytester.runpytest(p, "--doctest-modules") + result.assert_outcomes(failed=1) + assert "Tacos!" in result.stdout.str() + def test_doctestmodule_external_and_issue116(self, pytester: Pytester): p = pytester.mkpydir("hello") p.joinpath("__init__.py").write_text( From 189fcb7ac6897f47a3ca60d81a755bfac34505a0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 7 Sep 2023 10:06:11 -0300 Subject: [PATCH 2/2] Account for Python 3.7 in the 7.4.x branch --- src/_pytest/doctest.py | 4 +++- testing/test_doctest.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 73ee483b382..ca41a98ea9c 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -545,7 +545,9 @@ def _from_module(self, module, object): Here we override `_from_module` to check the underlying function instead. https://github.com/python/cpython/issues/107995 """ - if isinstance(object, functools.cached_property): + if hasattr(functools, "cached_property") and isinstance( + object, functools.cached_property + ): object = object.func # Type ignored because this is a private function. diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 2cf5b019c0a..665bdb73b5d 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -482,6 +482,9 @@ def test_doctestmodule(self, pytester: Pytester): reprec = pytester.inline_run(p, "--doctest-modules") reprec.assertoutcome(failed=1) + @pytest.mark.skipif( + sys.version_info[:2] <= (3, 7), reason="Only Python 3.7 or less" + ) def test_doctest_cached_property(self, pytester: Pytester): p = pytester.makepyfile( """