From 5a93a9f9e7e8d1cbcbfc93783e86572b6c678152 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Thu, 19 Sep 2024 09:43:50 -0300 Subject: [PATCH] Fix inference regression with property setters (#2567) Closes pylint-dev/pylint#9811 --- ChangeLog | 3 +++ astroid/nodes/scoped_nodes/scoped_nodes.py | 12 +++++++++++- tests/test_inference.py | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index d4b2ca245..d2c93f1ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,9 @@ What's New in astroid 3.3.3? ============================ Release date: TBA +* Fix inference regression with property setters + + Closes pylint-dev/pylint#9811 What's New in astroid 3.3.2? diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py index 629d524f9..5309f0a0d 100644 --- a/astroid/nodes/scoped_nodes/scoped_nodes.py +++ b/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -2487,6 +2487,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] @@ -2510,7 +2520,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 a8b11b161..185ee0f65 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( """