From 2dd4196f5123cf1febf66dcd43620cbf1504fa96 Mon Sep 17 00:00:00 2001 From: Andrew Haigh Date: Mon, 12 Apr 2021 18:49:18 +1000 Subject: [PATCH] Fix inference of properties in a class context Ref #940. If we are accessing an attribute and it is found on type(A).__dict__ *and* it is a data descriptor, then we resolve that descriptor. For the case of inferring a property which is accessed as a class attribute, this equates to the property being defined on the metaclass (which may be anywhere in the class hierarchy). --- astroid/scoped_nodes.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py index 333f42fe55..27237e8296 100644 --- a/astroid/scoped_nodes.py +++ b/astroid/scoped_nodes.py @@ -2554,7 +2554,7 @@ def igetattr(self, name, context=None, class_context=True): context = contextmod.copy_context(context) context.lookupname = name - metaclass = self.declared_metaclass(context=context) + metaclass = self.metaclass(context=context) try: attributes = self.getattr(name, context, class_context=class_context) # If we have more than one attribute, make sure that those starting from @@ -2587,9 +2587,12 @@ def igetattr(self, name, context=None, class_context=True): yield from function.infer_call_result( caller=self, context=context ) - # If we have a metaclass, we're accessing this attribute through - # the class itself, which means we can solve the property - elif metaclass: + # If we're in a class context, we need to determine if the property + # was defined in the metaclass (a derived class must be a subclass of + # the metaclass of all its bases), in which case we can resolve the + # property. If not, i.e. the property is defined in some base class + # instead, then we return the property object + elif metaclass and function.parent.scope() is metaclass: # Resolve a property as long as it is not accessed through # the class itself. yield from function.infer_call_result(