From 971913be3597543ea2e830ce1cc0839423666b7b Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 1 Jan 2021 15:57:55 +0100 Subject: [PATCH] Make it possible to use __getitem__ in interpreter --- jedi/inference/value/instance.py | 40 ++++++++++++++----------------- test/test_api/test_interpreter.py | 13 ++++++++++ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index c2c20359..ac05288f 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -121,7 +121,13 @@ class AbstractInstanceValue(Value): return [s.bind(self) for s in call_funcs.get_signatures()] 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 [] def execute_function_slots(self, names, *inferred_args): @@ -133,6 +139,17 @@ class AbstractInstanceValue(Value): def get_type_hint(self, add_class_info=True): 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): return "<%s of %s>" % (self.__class__.__name__, self.class_value) @@ -237,17 +254,6 @@ class _BaseTreeInstance(AbstractInstanceValue): or self.get_function_slot_names('__getattribute__')) 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): iter_slot_names = self.get_function_slot_names('__iter__') if not iter_slot_names: @@ -295,16 +301,6 @@ class _BaseTreeInstance(AbstractInstanceValue): else: 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): def __init__(self, inference_state, parent_context, class_value, arguments): diff --git a/test/test_api/test_interpreter.py b/test/test_api/test_interpreter.py index 4615764e..ecf10e97 100644 --- a/test/test_api/test_interpreter.py +++ b/test/test_api/test_interpreter.py @@ -599,6 +599,19 @@ def test_dict_getitem(code, 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(): raise KeyError