Skip to content

Commit cfd9e74

Browse files
authored
Add is_dataclass attribute to ClassDef (#1391)
1 parent 552f1c1 commit cfd9e74

File tree

4 files changed

+38
-1
lines changed

4 files changed

+38
-1
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Release date: TBA
3030

3131
Closes #1330
3232

33+
* Add ``is_dataclass`` attribute to ``ClassDef`` nodes.
34+
3335
* Use ``sysconfig`` instead of ``distutils`` to determine the location of
3436
python stdlib files and packages.
3537

astroid/brain/brain_dataclasses.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ def is_decorated_with_dataclass(node, decorator_names=DATACLASSES_DECORATORS):
6767

6868
def dataclass_transform(node: ClassDef) -> None:
6969
"""Rewrite a dataclass to be easily understood by pylint"""
70+
node.is_dataclass = True
7071

7172
for assign_node in _get_dataclass_attributes(node):
7273
name = assign_node.target.name

astroid/nodes/scoped_nodes/scoped_nodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2134,7 +2134,7 @@ def my_meth(self, arg):
21342134
":type: str"
21352135
),
21362136
)
2137-
_other_fields = ("name", "doc")
2137+
_other_fields = ("name", "doc", "is_dataclass")
21382138
_other_other_fields = ("locals", "_newstyle")
21392139
_newstyle = None
21402140

@@ -2212,6 +2212,9 @@ def __init__(
22122212
:type doc: str or None
22132213
"""
22142214

2215+
self.is_dataclass: bool = False
2216+
"""Whether this class is a dataclass."""
2217+
22152218
super().__init__(
22162219
lineno=lineno,
22172220
col_offset=col_offset,

tests/unittest_brain_dataclasses.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class A:
184184
inferred = next(class_def.infer())
185185
assert isinstance(inferred, nodes.ClassDef)
186186
assert inferred.instance_attrs == {}
187+
assert inferred.is_dataclass
187188

188189
# Both the class and instance can still access the attribute
189190
for node in (klass, instance):
@@ -216,6 +217,7 @@ class A:
216217
inferred = next(class_def.infer())
217218
assert isinstance(inferred, nodes.ClassDef)
218219
assert inferred.instance_attrs == {}
220+
assert inferred.is_dataclass
219221

220222
# Both the class and instance can still access the attribute
221223
for node in (klass, instance):
@@ -248,6 +250,7 @@ class A:
248250
inferred = next(class_def.infer())
249251
assert isinstance(inferred, nodes.ClassDef)
250252
assert inferred.instance_attrs == {}
253+
assert inferred.is_dataclass
251254

252255
# Both the class and instance can still access the attribute
253256
for node in (klass, instance):
@@ -666,6 +669,7 @@ class A:
666669
inferred = node.inferred()
667670
assert len(inferred) == 1 and isinstance(inferred[0], nodes.ClassDef)
668671
assert "attribute" in inferred[0].instance_attrs
672+
assert inferred[0].is_dataclass
669673

670674

671675
@parametrize_module
@@ -683,3 +687,30 @@ class A:
683687
inferred = code.inferred()
684688
assert len(inferred) == 1
685689
assert isinstance(inferred[0], nodes.ClassDef)
690+
assert inferred[0].is_dataclass
691+
692+
693+
def test_non_dataclass_is_not_dataclass() -> None:
694+
"""Test that something that isn't a dataclass has the correct attribute."""
695+
module = astroid.parse(
696+
"""
697+
class A:
698+
val: field()
699+
700+
def dataclass():
701+
return
702+
703+
@dataclass
704+
class B:
705+
val: field()
706+
"""
707+
)
708+
class_a = module.body[0].inferred()
709+
assert len(class_a) == 1
710+
assert isinstance(class_a[0], nodes.ClassDef)
711+
assert not class_a[0].is_dataclass
712+
713+
class_b = module.body[2].inferred()
714+
assert len(class_b) == 1
715+
assert isinstance(class_b[0], nodes.ClassDef)
716+
assert not class_b[0].is_dataclass

0 commit comments

Comments
 (0)