diff --git a/ChangeLog b/ChangeLog index fdd60ec09e..fcced3bc2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,12 +13,15 @@ What's New in astroid 3.3.3? ============================ Release date: TBA +* Fix inference regression with property setters. + + Closes pylint-dev/pylint#9811 + * Add annotation-only instance attributes to attrs classes to fix `no-member` false positives. Closes #2514 - What's New in astroid 3.3.2? ============================ Release date: 2024-08-11 diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index af3b9d39a8..d733a6ae21 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -2506,6 +2506,16 @@ def igetattr( if attr.parent and attr.parent.scope() == first_scope ] functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] + setter = None + for function in functions: + dec_names = function.decoratornames(context=context) + for dec_name in dec_names: + if dec_name is util.Uninferable: + continue + if dec_name.split(".")[-1] == "setter": + setter = function + if setter: + break if functions: # Prefer only the last function, unless a property is involved. last_function = functions[-1] @@ -2529,7 +2539,7 @@ def igetattr( elif isinstance(inferred, objects.Property): function = inferred.function if not class_context: - if not context.callcontext: + if not context.callcontext and not setter: context.callcontext = CallContext( args=function.args.arguments, callee=function ) diff --git a/tests/test_inference.py b/tests/test_inference.py index a8b11b1614..185ee0f650 100644 --- a/tests/test_inference.py +++ b/tests/test_inference.py @@ -4416,6 +4416,23 @@ def func(): inferred = list(node.inferred()) assert [const.value for const in inferred] == [42, False] + def test_infer_property_setter(self) -> None: + node = extract_node( + """ + class PropertyWithSetter: + @property + def host(self): + return self._host + + @host.setter + def host(self, value: str): + self._host = value + + PropertyWithSetter().host #@ + """ + ) + assert not isinstance(next(node.infer()), Instance) + def test_delayed_attributes_without_slots(self) -> None: ast_node = extract_node( """