2626)
2727ATTR_NOT_IN_DOCSTR_CODE = f"{ ERROR_CODE_PREFIX } 063"
2828ATTR_NOT_IN_DOCSTR_MSG = (
29- f'{ ATTR_NOT_IN_DOCSTR_CODE } "%s" attribute should be described in the docstring'
29+ f'{ ATTR_NOT_IN_DOCSTR_CODE } "%s" attribute/ property should be described in the docstring'
3030 f"{ MORE_INFO_BASE } { ATTR_NOT_IN_DOCSTR_CODE .lower ()} "
3131)
3232ATTR_IN_DOCSTR_CODE = f"{ ERROR_CODE_PREFIX } 064"
3939PRIVATE_ATTR_PREFIX = "_"
4040
4141
42+ def is_property_decorator (node : ast .expr ) -> bool :
43+ """Determine whether an expression is a property decorator.
44+
45+ Args:
46+ node: The node to check.
47+
48+ Returns:
49+ Whether the node is a property decorator.
50+ """
51+ if isinstance (node , ast .Name ):
52+ return node .id == "property"
53+
54+ # Handle call
55+ if isinstance (node , ast .Call ):
56+ return is_property_decorator (node = node .func )
57+
58+ return False
59+
60+
4261def _get_class_target_name (target : ast .expr ) -> ast .Name | None :
4362 """Get the name of the target for an assignment on the class.
4463
@@ -61,7 +80,7 @@ def _get_class_target_name(target: ast.expr) -> ast.Name | None:
6180
6281
6382def _iter_class_attrs (
64- nodes : Iterable [ast .Assign | ast .AnnAssign | ast .AugAssign ],
83+ nodes : Iterable [ast .Assign | ast .AnnAssign | ast .AugAssign | types_ . Node ],
6584) -> Iterator [types_ .Node ]:
6685 """Get the node of the variable being assigned at the class level if the target is a Name.
6786
@@ -72,7 +91,9 @@ def _iter_class_attrs(
7291 All the nodes of name targets of the assignment expressions.
7392 """
7493 for node in nodes :
75- if isinstance (node , ast .Assign ):
94+ if isinstance (node , types_ .Node ):
95+ yield node
96+ elif isinstance (node , ast .Assign ):
7697 target_names = filter (
7798 None , (_get_class_target_name (target ) for target in node .targets )
7899 )
@@ -136,7 +157,7 @@ def _iter_method_attrs(
136157def check (
137158 docstr_info : docstring .Docstring ,
138159 docstr_node : ast .Constant ,
139- class_assign_nodes : Iterable [ast .Assign | ast .AnnAssign | ast .AugAssign ],
160+ class_assign_nodes : Iterable [ast .Assign | ast .AnnAssign | ast .AugAssign | types_ . Node ],
140161 method_assign_nodes : Iterable [ast .Assign | ast .AnnAssign | ast .AugAssign ],
141162) -> Iterator [types_ .Problem ]:
142163 """Check that all class attributes are described in the docstring.
@@ -211,7 +232,7 @@ class VisitorWithinClass(ast.NodeVisitor):
211232 method_assign_nodes: All the return nodes encountered within the class methods.
212233 """
213234
214- class_assign_nodes : list [ast .Assign | ast .AnnAssign | ast .AugAssign ]
235+ class_assign_nodes : list [ast .Assign | ast .AnnAssign | ast .AugAssign | types_ . Node ]
215236 method_assign_nodes : list [ast .Assign | ast .AnnAssign | ast .AugAssign ]
216237 _visited_once : bool
217238 _visited_top_level : bool
@@ -237,6 +258,18 @@ def visit_assign(self, node: ast.Assign | ast.AnnAssign | ast.AugAssign) -> None
237258 # Ensure recursion continues
238259 self .generic_visit (node )
239260
261+ def visit_any_function (self , node : ast .FunctionDef | ast .AsyncFunctionDef ) -> None :
262+ """Visit a function definition node.
263+
264+ Args:
265+ node: The function definition to check.
266+ """
267+ if any (is_property_decorator (decorator ) for decorator in node .decorator_list ):
268+ self .class_assign_nodes .append (
269+ types_ .Node (lineno = node .lineno , col_offset = node .col_offset , name = node .name )
270+ )
271+ self .visit_top_level (node = node )
272+
240273 def visit_once (self , node : ast .AST ) -> None :
241274 """Visit the node once and then skip.
242275
@@ -264,6 +297,6 @@ def visit_top_level(self, node: ast.AST) -> None:
264297 visit_AnnAssign = visit_assign # noqa: N815,DCO063
265298 visit_AugAssign = visit_assign # noqa: N815,DCO063
266299 # Ensure that nested functions and classes are not iterated over
267- visit_FunctionDef = visit_top_level # noqa: N815,DCO063
268- visit_AsyncFunctionDef = visit_top_level # noqa: N815,DCO063
300+ visit_FunctionDef = visit_any_function # noqa: N815,DCO063
301+ visit_AsyncFunctionDef = visit_any_function # noqa: N815,DCO063
269302 visit_ClassDef = visit_once # noqa: N815,DCO063
0 commit comments