|
21 | 21 |
|
22 | 22 | from pylint import constants |
23 | 23 | from pylint.pyreverse import utils |
| 24 | +from pylint.pyreverse.utils import get_annotation_label |
24 | 25 |
|
25 | 26 | _WrapperFuncT = Callable[ |
26 | 27 | [Callable[[str], nodes.Module], str, bool], Optional[nodes.Module] |
@@ -174,6 +175,51 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: |
174 | 175 | if not isinstance(assignattr, nodes.Unknown): |
175 | 176 | self.associations_handler.handle(assignattr, node) |
176 | 177 | self.handle_assignattr_type(assignattr, node) |
| 178 | + # resolve class properties |
| 179 | + properties = [] |
| 180 | + for name, member_list in node.locals.items(): |
| 181 | + for member in member_list: |
| 182 | + if isinstance(member, nodes.FunctionDef): |
| 183 | + # Check if this function is decorated with @property |
| 184 | + if any( |
| 185 | + isinstance(decorator, nodes.Name) |
| 186 | + and decorator.name == "property" |
| 187 | + for decorator in ( |
| 188 | + member.decorators.nodes if member.decorators else [] |
| 189 | + ) |
| 190 | + ): |
| 191 | + properties.append(member) |
| 192 | + |
| 193 | + # Extract return type directly from the function definition |
| 194 | + if member.returns: |
| 195 | + # Use get_annotation_label to extract the type name |
| 196 | + annotation_label = get_annotation_label(member.returns) |
| 197 | + inferred_type = annotation_label |
| 198 | + |
| 199 | + # Special handling for enum types ==> name property is always a string |
| 200 | + elif ( |
| 201 | + any(base.name == "Enum" for base in node.ancestors()) |
| 202 | + and name == "name" |
| 203 | + ): |
| 204 | + inferred_type = "str" |
| 205 | + |
| 206 | + # Fallback to inference (which may not always be perfect) |
| 207 | + else: |
| 208 | + inferred_nodes = utils.infer_node(member) |
| 209 | + inferred_type = ( |
| 210 | + ", ".join(str(inf) for inf in inferred_nodes if inf) |
| 211 | + if inferred_nodes |
| 212 | + else "Unknown" |
| 213 | + ) |
| 214 | + |
| 215 | + # Assign to instance attributes with the inferred or annotated type |
| 216 | + node.instance_attrs_type[name] = [inferred_type] |
| 217 | + print( |
| 218 | + f"Property {name} in class {node.name} has type {inferred_type}" |
| 219 | + ) |
| 220 | + |
| 221 | + # Add the detected properties to a new attribute for the class definition |
| 222 | + node.properties = properties |
177 | 223 |
|
178 | 224 | def visit_functiondef(self, node: nodes.FunctionDef) -> None: |
179 | 225 | """Visit an astroid.Function node. |
|
0 commit comments