diff --git a/ChangeLog b/ChangeLog index 60bca91fdd..a41a6aa0ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,10 @@ What's New in astroid 4.0.3? ============================ 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 bafd5f1c4f..244665e080 100644 --- a/astroid/brain/brain_dataclasses.py +++ b/astroid/brain/brain_dataclasses.py @@ -54,7 +54,7 @@ def is_decorated_with_dataclass( ) -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 @@ def dataclass_transform(node: nodes.ClassDef) -> None: 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 @@ def dataclass_transform(node: nodes.ClassDef) -> None: 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 875ee057b5..f3ac0a6fd6 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 @@ class E(C[str], D): ... 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( """