Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Release date: TBA

Closes #1330

* Add ``is_dataclass`` attribute to ``ClassDef`` nodes.

* Use ``sysconfig`` instead of ``distutils`` to determine the location of
python stdlib files and packages.

Expand Down
1 change: 1 addition & 0 deletions astroid/brain/brain_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def is_decorated_with_dataclass(node, decorator_names=DATACLASSES_DECORATORS):

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

for assign_node in _get_dataclass_attributes(node):
name = assign_node.target.name
Expand Down
5 changes: 4 additions & 1 deletion astroid/nodes/scoped_nodes/scoped_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2134,7 +2134,7 @@ def my_meth(self, arg):
":type: str"
),
)
_other_fields = ("name", "doc")
_other_fields = ("name", "doc", "is_dataclass")
_other_other_fields = ("locals", "_newstyle")
_newstyle = None

Expand Down Expand Up @@ -2225,6 +2225,9 @@ def __init__(
for local_name, node in self.implicit_locals():
self.add_local_node(node, local_name)

self.is_dataclass: bool = False
"""Whether this class is a dataclass."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this directly below self.doc = doc. That way all "initial" assignments are grouped together.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:+1


def implicit_parameters(self):
return 1

Expand Down
30 changes: 30 additions & 0 deletions tests/unittest_brain_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class A:
inferred = next(class_def.infer())
assert isinstance(inferred, nodes.ClassDef)
assert inferred.instance_attrs == {}
assert inferred.is_dataclass
Comment thread
cdce8p marked this conversation as resolved.

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

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

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


@parametrize_module
Expand All @@ -683,3 +687,29 @@ class A:
inferred = code.inferred()
assert len(inferred) == 1
assert isinstance(inferred[0], nodes.ClassDef)
assert inferred[0].is_dataclass


def test_non_dataclass_is_not_dataclass() -> None:
"""Test that something that isn't a dataclass has the correct attribute."""
module = astroid.parse(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also use extract_node together with #@. That would only return selected nodes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change it. I always use parse, but don't have a preference.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both work fine. extract_node might just be a bit easier to annotate if you only selected one node type.

"""
class A:
val: field()

def dataclass():
return

@dataclass
class B:
val: field()
"""
)
inferred = module.body[0].inferred()
assert len(inferred) == 1
assert isinstance(inferred[0], nodes.ClassDef)
assert not inferred[0].is_dataclass
inferred = module.body[2].inferred()
assert len(inferred) == 1
assert isinstance(inferred[0], nodes.ClassDef)
assert not inferred[0].is_dataclass