[Backport maintenance/4.0.x] Fix base class inference for dataclasses with PEP 695 syntax (#2919)
(cherry picked from commit 3774c0a788ca665ecd7d28a064e56651efc4a3c1)
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
diff --git a/ChangeLog b/ChangeLog
index 60bca91..a41a6aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -13,6 +13,10 @@
============================
Release date: TBA
+* Fix base class inference for dataclasses using the PEP 695 typing syntax.
+
+ Refs pylint-dev/pylint#10788
+
What's New in astroid 4.0.2?
diff --git a/astroid/brain/brain_dataclasses.py b/astroid/brain/brain_dataclasses.py
index bafd5f1..244665e 100644
--- a/astroid/brain/brain_dataclasses.py
+++ b/astroid/brain/brain_dataclasses.py
@@ -54,7 +54,7 @@
)
-def dataclass_transform(node: nodes.ClassDef) -> None:
+def dataclass_transform(node: nodes.ClassDef) -> nodes.ClassDef | None:
"""Rewrite a dataclass to be easily understood by pylint."""
node.is_dataclass = True
@@ -70,7 +70,7 @@
node.instance_attrs[name] = [rhs_node]
if not _check_generate_dataclass_init(node):
- return
+ return None
kw_only_decorated = False
if node.decorators.nodes:
@@ -102,6 +102,7 @@
new_assign = parse(f"{DEFAULT_FACTORY} = object()").body[0]
new_assign.parent = root
root.locals[DEFAULT_FACTORY] = [new_assign.targets[0]]
+ return node
def _get_dataclass_attributes(
diff --git a/tests/test_scoped_nodes.py b/tests/test_scoped_nodes.py
index 875ee05..f3ac0a6 100644
--- a/tests/test_scoped_nodes.py
+++ b/tests/test_scoped_nodes.py
@@ -29,7 +29,7 @@
util,
)
from astroid.bases import BoundMethod, Generator, Instance, UnboundMethod
-from astroid.const import WIN32
+from astroid.const import PY312_PLUS, WIN32
from astroid.exceptions import (
AstroidBuildingError,
AttributeInferenceError,
@@ -1967,6 +1967,34 @@
cls, [".E", ".C", ".A", ".B", "typing.Generic", ".D", "builtins.object"]
)
+ @pytest.mark.skipif(not PY312_PLUS, reason="PEP 695 syntax requires Python 3.12")
+ def test_mro_generic_8(self):
+ cls = builder.extract_node(
+ """
+ class A: ...
+ class B[T]: ...
+ class C[T](A, B[T]): ...
+ """
+ )
+ assert isinstance(cls, nodes.ClassDef)
+ self.assertEqualMroQName(cls, [".C", ".A", ".B", "builtins.object"])
+
+ @pytest.mark.skipif(not PY312_PLUS, reason="PEP 695 syntax requires Python 3.12")
+ def test_mro_generic_9(self):
+ cls = builder.extract_node(
+ """
+ from dataclasses import dataclass
+ @dataclass
+ class A: ...
+ @dataclass
+ class B[T]: ...
+ @dataclass
+ class C[T](A, B[T]): ...
+ """
+ )
+ assert isinstance(cls, nodes.ClassDef)
+ self.assertEqualMroQName(cls, [".C", ".A", ".B", "builtins.object"])
+
def test_mro_generic_error_1(self):
cls = builder.extract_node(
"""