Move __getattr__ and __getattribute__ logic to instance

Now getattr warnings might be wrong
This commit is contained in:
Dave Halter
2019-08-23 21:59:01 +02:00
parent 60a73f6bac
commit 0992dc7ae9
2 changed files with 25 additions and 20 deletions

View File

@@ -121,30 +121,10 @@ class NameFinder(object):
self._string_name, self._context, names, self._position) self._string_name, self._context, names, self._position)
return list(names) 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): def _names_to_types(self, names):
values = ValueSet.from_sets(name.infer() for name in names) values = ValueSet.from_sets(name.infer() for name in names)
debug.dbg('finder._names_to_types: %s -> %s', names, values) 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. # Add isinstance and other if/assert knowledge.
if not values and isinstance(self._name, tree.Name) and \ if not values and isinstance(self._name, tree.Name) and \
not self._name_context.is_instance() and not self._context.is_compiled(): not self._name_context.is_instance() and not self._context.is_compiled():

View File

@@ -1,5 +1,7 @@
from abc import abstractproperty from abc import abstractproperty
from parso.python.tree import Name
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi.inference import compiled from jedi.inference import compiled
@@ -309,6 +311,29 @@ class TreeInstance(AbstractInstanceValue):
for signature in init.get_signatures(): for signature in init.get_signatures():
yield signature.value 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): class AnonymousInstance(TreeInstance):
def __init__(self, inference_state, parent_context, class_value): def __init__(self, inference_state, parent_context, class_value):