Repository: numpy/numpydoc

Issue:
BUG: Numpydoc doesn't render attributes decorated with `cached_property` in the Attributes section
Numpydoc doesn't render attributes decorated with `cached_property` in the `Attribute` section. I think it is due to this line:

https://github.com/numpy/numpydoc/blob/b138ea7758532a8b6950c4404570ab367c3919f4/numpydoc/docscrape.py#L709

which can be changed to:

```
from functools import cached_property

...


                    func is None
                    or isinstance(func, property)
                    or isinstance(func, cached_property)
                    or inspect.isdatadescriptor(func)
```

to fix the issue.

xref scipy/scipy#16002

Comments:

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index cc83b2a..b2269f4 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,32 +2,39 @@ version: 2
 jobs:
   build_docs:
     docker:
-      - image: circleci/python:3.7-stretch
+      - image: "cimg/python:3.10"
     steps:
       - checkout
       - run:
-          name: Set BASH_ENV
-          command: |
-            echo "set -e" >> $BASH_ENV;
-            echo "export PATH=~/.local/bin:$PATH" >> $BASH_ENV;
-            sudo apt update
-            sudo apt install dvipng texlive-fonts-recommended texlive-latex-recommended texlive-latex-extra texlive-generic-extra latexmk texlive-xetex
+          name: Update apt-get
+          command: sudo apt-get update
+      - run:
+          name: Install TeX
+          command: sudo apt install dvipng texlive-fonts-recommended texlive-latex-recommended texlive-latex-extra latexmk texlive-xetex
       - restore_cache:
           keys:
             - pip-cache
       - run:
           name: Get dependencies and install
           command: |
-            pip install --user -q --upgrade pip setuptools
-            pip install --user -q --upgrade numpy matplotlib sphinx pydata-sphinx-theme
-            pip install --user -e .
+            python3 -m venv venv
+            source venv/bin/activate
+            python -m pip install --upgrade pip wheel setuptools
+            python -m pip install --upgrade -r requirements/doc.txt
+            python -m pip list
       - save_cache:
           key: pip-cache
           paths:
             - ~/.cache/pip
       - run:
-          name: make html
+          name: Install
+          command: |
+            source venv/bin/activate
+            pip install -e .
+      - run:
+          name: Build docs
           command: |
+            source venv/bin/activate
             make -C doc html
       - store_artifacts:
           path: doc/_build/html/
@@ -35,6 +42,7 @@ jobs:
       - run:
           name: make tinybuild
           command: |
+            source venv/bin/activate
             make -C numpydoc/tests/tinybuild html
       - store_artifacts:
           path: numpydoc/tests/tinybuild/_build/html/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5bc6fa2..611379e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -17,7 +17,7 @@ repos:
       - id: check-added-large-files
 
   - repo: https://github.com/psf/black
-    rev: 22.6.0
+    rev: 22.8.0
     hooks:
       - id: black
 
@@ -34,7 +34,7 @@ repos:
       - id: blacken-docs
 
   - repo: https://github.com/asottile/pyupgrade
-    rev: v2.37.1
+    rev: v2.38.2
     hooks:
       - id: pyupgrade
         args: [--py37-plus]
diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py
index 9496f9d..e5c07f5 100644
--- a/numpydoc/docscrape.py
+++ b/numpydoc/docscrape.py
@@ -12,6 +12,13 @@ import copy
 import sys
 
 
+# TODO: Remove try-except when support for Python 3.7 is dropped
+try:
+    from functools import cached_property
+except ImportError:  # cached_property added in Python 3.8
+    cached_property = property
+
+
 def strip_blank_lines(l):
     "Remove leading and trailing blank lines from a list of lines"
     while l and not l[0].strip():
@@ -706,7 +713,7 @@ class ClassDoc(NumpyDocString):
                 not name.startswith("_")
                 and (
                     func is None
-                    or isinstance(func, property)
+                    or isinstance(func, (property, cached_property))
                     or inspect.isdatadescriptor(func)
                 )
                 and self._is_show_member(name)


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 437afca..24fe29c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -26,7 +26,7 @@ jobs:
       - name: Setup environment
         run: |
           python -m pip install --upgrade pip wheel setuptools
-          python -m pip install -r requirements/test.txt -r doc/requirements.txt
+          python -m pip install -r requirements/test.txt -r requirements/doc.txt
           python -m pip install codecov
           python -m pip install ${{ matrix.sphinx-version }}
           python -m pip list
@@ -116,7 +116,7 @@ jobs:
       - name: Setup environment
         run: |
           python -m pip install --upgrade pip wheel setuptools
-          python -m pip install --pre -r requirements/test.txt -r doc/requirements.txt
+          python -m pip install --pre -r requirements/test.txt -r requirements/doc.txt
           python -m pip install codecov
           python -m pip list
 
diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py
index 049d2a2..227f872 100644
--- a/numpydoc/tests/test_docscrape.py
+++ b/numpydoc/tests/test_docscrape.py
@@ -1,6 +1,7 @@
 from collections import namedtuple
 from copy import deepcopy
 import re
+import sys
 import textwrap
 import warnings
 
@@ -1624,6 +1625,26 @@ def test__error_location_no_name_attr():
         nds._error_location(msg=msg)
 
 
+@pytest.mark.skipif(
+    sys.version_info < (3, 8), reason="cached_property was added in 3.8"
+)
+def test_class_docstring_cached_property():
+    """Ensure that properties marked with the `cached_property` decorator
+    are listed in the Methods section. See gh-432."""
+    from functools import cached_property
+
+    class Foo:
+        _x = [1, 2, 3]
+
+        @cached_property
+        def val(self):
+            return self._x
+
+    class_docstring = get_doc_object(Foo)
+    assert len(class_docstring["Attributes"]) == 1
+    assert class_docstring["Attributes"][0].name == "val"
+
+
 if __name__ == "__main__":
     import pytest