Make it possible to use __getitem__ in interpreter

This commit is contained in:
Dave Halter
2021-01-01 15:57:55 +01:00
parent 36ea6b3285
commit 971913be35
2 changed files with 31 additions and 22 deletions

View File

@@ -121,7 +121,13 @@ class AbstractInstanceValue(Value):
return [s.bind(self) for s in call_funcs.get_signatures()] return [s.bind(self) for s in call_funcs.get_signatures()]
def get_function_slot_names(self, name): def get_function_slot_names(self, name):
# Searches for Python functions in classes. # Python classes don't look at the dictionary of the instance when
# looking up `__call__`. This is something that has to do with Python's
# internal slot system (note: not __slots__, but C slots).
for filter in self.get_filters(include_self_names=False):
names = filter.get(name)
if names:
return names
return [] return []
def execute_function_slots(self, names, *inferred_args): def execute_function_slots(self, names, *inferred_args):
@@ -133,6 +139,17 @@ class AbstractInstanceValue(Value):
def get_type_hint(self, add_class_info=True): def get_type_hint(self, add_class_info=True):
return self.py__name__() return self.py__name__()
def py__getitem__(self, index_value_set, contextualized_node):
names = self.get_function_slot_names('__getitem__')
if not names:
return super().py__getitem__(
index_value_set,
contextualized_node,
)
args = ValuesArguments([index_value_set])
return ValueSet.from_sets(name.infer().execute(args) for name in names)
def __repr__(self): def __repr__(self):
return "<%s of %s>" % (self.__class__.__name__, self.class_value) return "<%s of %s>" % (self.__class__.__name__, self.class_value)
@@ -237,17 +254,6 @@ class _BaseTreeInstance(AbstractInstanceValue):
or self.get_function_slot_names('__getattribute__')) or self.get_function_slot_names('__getattribute__'))
return self.execute_function_slots(names, name) return self.execute_function_slots(names, name)
def py__getitem__(self, index_value_set, contextualized_node):
names = self.get_function_slot_names('__getitem__')
if not names:
return super().py__getitem__(
index_value_set,
contextualized_node,
)
args = ValuesArguments([index_value_set])
return ValueSet.from_sets(name.infer().execute(args) for name in names)
def py__iter__(self, contextualized_node=None): def py__iter__(self, contextualized_node=None):
iter_slot_names = self.get_function_slot_names('__iter__') iter_slot_names = self.get_function_slot_names('__iter__')
if not iter_slot_names: if not iter_slot_names:
@@ -295,16 +301,6 @@ class _BaseTreeInstance(AbstractInstanceValue):
else: else:
return ValueSet([self]) return ValueSet([self])
def get_function_slot_names(self, name):
# Python classes don't look at the dictionary of the instance when
# looking up `__call__`. This is something that has to do with Python's
# internal slot system (note: not __slots__, but C slots).
for filter in self.get_filters(include_self_names=False):
names = filter.get(name)
if names:
return names
return []
class TreeInstance(_BaseTreeInstance): class TreeInstance(_BaseTreeInstance):
def __init__(self, inference_state, parent_context, class_value, arguments): def __init__(self, inference_state, parent_context, class_value, arguments):

View File

@@ -599,6 +599,19 @@ def test_dict_getitem(code, types):
assert [c.name for c in comps] == types assert [c.name for c in comps] == types
@pytest.mark.parametrize('class_is_findable', [False, True])
def test__getitem__(class_is_findable):
class GetitemCls:
def __getitem__(self, key) -> int:
pass
if not class_is_findable:
GetitemCls.__name__ = 'asdf'
n, = jedi.Interpreter('GetitemCls()[0]', [locals()]).infer()
assert n.name == 'int'
def foo(): def foo():
raise KeyError raise KeyError