From 0992dc7ae9b71f71ab073818359286a5e2a564ac Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 23 Aug 2019 21:59:01 +0200 Subject: [PATCH] Move __getattr__ and __getattribute__ logic to instance Now getattr warnings might be wrong --- jedi/inference/finder.py | 20 -------------------- jedi/inference/value/instance.py | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/jedi/inference/finder.py b/jedi/inference/finder.py index 7c7ccf6e..e8f05e4b 100644 --- a/jedi/inference/finder.py +++ b/jedi/inference/finder.py @@ -121,30 +121,10 @@ class NameFinder(object): self._string_name, self._context, names, self._position) return list(names) - def _check_getattr(self, inst): - """Checks for both __getattr__ and __getattribute__ methods""" - # str is important, because it shouldn't be `Name`! - name = compiled.create_simple_object(self._inference_state, self._string_name) - - # This is a little bit special. `__getattribute__` is in Python - # executed before `__getattr__`. But: I know no use case, where - # this could be practical and where Jedi would return wrong types. - # If you ever find something, let me know! - # We are inversing this, because a hand-crafted `__getattribute__` - # could still call another hand-crafted `__getattr__`, but not the - # other way around. - names = (inst.get_function_slot_names(u'__getattr__') or - inst.get_function_slot_names(u'__getattribute__')) - return inst.execute_function_slots(names, name) - def _names_to_types(self, names): values = ValueSet.from_sets(name.infer() for name in names) debug.dbg('finder._names_to_types: %s -> %s', names, values) - if not names and self._context.is_instance() and not self._context.is_compiled(): - # handling __getattr__ / __getattribute__ - return self._check_getattr(self._context) - # Add isinstance and other if/assert knowledge. if not values and isinstance(self._name, tree.Name) and \ not self._name_context.is_instance() and not self._context.is_compiled(): diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index e08e7581..97ec310b 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -1,5 +1,7 @@ from abc import abstractproperty +from parso.python.tree import Name + from jedi import debug from jedi import settings from jedi.inference import compiled @@ -309,6 +311,29 @@ class TreeInstance(AbstractInstanceValue): for signature in init.get_signatures(): yield signature.value + def py__getattribute__(self, name_or_str, *args, **kwargs): + inferred = super(AbstractInstanceValue, self).py__getattribute__( + name_or_str, *args, **kwargs) + if inferred or self.is_stub(): + return inferred + + # Since nothing was inferred, now check the __getattr__ and + # __getattribute__ methods. Stubs don't need to be checked, because + # they don't contain any logic. + n = name_or_str.value if isinstance(name_or_str, Name) else name_or_str + name = compiled.create_simple_object(self.inference_state, n) + + # This is a little bit special. `__getattribute__` is in Python + # executed before `__getattr__`. But: I know no use case, where + # this could be practical and where Jedi would return wrong types. + # If you ever find something, let me know! + # We are inversing this, because a hand-crafted `__getattribute__` + # could still call another hand-crafted `__getattr__`, but not the + # other way around. + names = (self.get_function_slot_names(u'__getattr__') or + self.get_function_slot_names(u'__getattribute__')) + return self.execute_function_slots(names, name) + class AnonymousInstance(TreeInstance): def __init__(self, inference_state, parent_context, class_value):