Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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 @@ -2212,6 +2212,9 @@ def __init__(
:type doc: str or None
"""

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

super().__init__(
lineno=lineno,
col_offset=col_offset,
Expand Down
26 changes: 26 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,25 @@ 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."""
ast_nodes = astroid.extract_node(

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.

Suggested change
ast_nodes = astroid.extract_node(
ast_nodes: List[nodes.NodeNG] = astroid.extract_node( # type: ignore[assignment]

extract_node can return either a list or an individual node. (Not the best API design 😕) Typing it as List[nodes.NodeNG] should help.

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.

That's why I added _extract_single_node in https://github.com/PyCQA/astroid/pull/1358/files. However, now that I think about it it should probably use an assert instead of returning the first element..
We could create extract_nodes as well?

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.

Something for another day. We have took many open problems already 😅

"""
class A: #@
val: field()

def dataclass():
return

@dataclass
class B: #@
val: field()
"""
)
assert isinstance(ast_nodes[0], nodes.ClassDef)
assert not ast_nodes[0].is_dataclass
assert isinstance(ast_nodes[1], nodes.ClassDef)
assert not ast_nodes[1].is_dataclass