Skip to content

Commit 512c8be

Browse files
authored
Enhanced support for @Property decorator in pyreverse (#10057)
* Extract return types for property decorator * Update writer logic to also add properties to the diagram * Revert "Update writer logic to also add properties to the diagram" This reverts commit a707767. Revert "Extract return types for property decorator" This reverts commit 9962519. * Implement a cleaner solution for extracting the return types of properties * Add functional tests for property decorator * Merge into a single file * Add newsfragment * Fix newsfragment * Fix file name of newsframent
1 parent 5f26f32 commit 512c8be

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

doc/whatsnew/fragments/10057.feature

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Enhanced support for @property decorator in pyreverse to correctly display return types of annotated properties when generating class diagrams.
2+
3+
Closes #10057

pylint/pyreverse/diagrams.py

+20-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from astroid import nodes, util
1414

1515
from pylint.checkers.utils import decorated_with_property, in_type_checking_block
16-
from pylint.pyreverse.utils import FilterMixIn
16+
from pylint.pyreverse.utils import FilterMixIn, get_annotation_label
1717

1818

1919
class Figure:
@@ -121,12 +121,16 @@ def get_relationship(
121121
def get_attrs(self, node: nodes.ClassDef) -> list[str]:
122122
"""Return visible attributes, possibly with class name."""
123123
attrs = []
124+
125+
# Collect functions decorated with @property
124126
properties = {
125127
local_name: local_node
126128
for local_name, local_node in node.items()
127129
if isinstance(local_node, nodes.FunctionDef)
128130
and decorated_with_property(local_node)
129131
}
132+
133+
# Add instance attributes to properties
130134
for attr_name, attr_type in list(node.locals_type.items()) + list(
131135
node.instance_attrs_type.items()
132136
):
@@ -136,9 +140,21 @@ def get_attrs(self, node: nodes.ClassDef) -> list[str]:
136140
for node_name, associated_nodes in properties.items():
137141
if not self.show_attr(node_name):
138142
continue
139-
names = self.class_names(associated_nodes)
140-
if names:
141-
node_name = f"{node_name} : {', '.join(names)}"
143+
144+
# Handle property methods differently to correctly extract return type
145+
if isinstance(
146+
associated_nodes, nodes.FunctionDef
147+
) and decorated_with_property(associated_nodes):
148+
if associated_nodes.returns:
149+
type_annotation = get_annotation_label(associated_nodes.returns)
150+
node_name = f"{node_name} : {type_annotation}"
151+
152+
# Handle regular attributes
153+
else:
154+
names = self.class_names(associated_nodes)
155+
if names:
156+
node_name = f"{node_name} : {', '.join(names)}"
157+
142158
attrs.append(node_name)
143159
return sorted(attrs)
144160

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
classDiagram
2+
class AnnotatedPropertyTest {
3+
x : int
4+
}
5+
class NonAnnotatedPropertyTest {
6+
x
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# property_decorator.py
2+
class AnnotatedPropertyTest:
3+
"""Test class for property decorators with annotated return type"""
4+
def __init__(self):
5+
self._x = 0
6+
7+
@property
8+
def x(self) -> int:
9+
"""This is a getter for x"""
10+
return self._x
11+
12+
@x.setter
13+
def x(self, value):
14+
"""This is a setter for x"""
15+
self._x = value
16+
17+
@x.deleter
18+
def x(self):
19+
"""This is a deleter for x"""
20+
del self._x
21+
22+
23+
class NonAnnotatedPropertyTest:
24+
"""Test class for property decorators without annotated return type"""
25+
def __init__(self):
26+
self._x = 0
27+
28+
@property
29+
def x(self):
30+
"""This is a getter for x"""
31+
return self._x
32+
33+
@x.setter
34+
def x(self, value):
35+
"""This is a setter for x"""
36+
self._x = value
37+
38+
@x.deleter
39+
def x(self):
40+
"""This is a deleter for x"""
41+
del self._x

0 commit comments

Comments
 (0)