1
0
forked from VimPlug/jedi

Fix issues with getitem on compiled objects that have annotations, see #1719

This commit is contained in:
Dave Halter
2021-01-17 13:47:36 +01:00
parent 47d0318fa6
commit 387d73990b
3 changed files with 29 additions and 3 deletions

View File

@@ -211,7 +211,22 @@ class DirectObjectAccess:
def py__getitem__all_values(self): def py__getitem__all_values(self):
if isinstance(self._obj, dict): if isinstance(self._obj, dict):
return [self._create_access_path(v) for v in self._obj.values()] return [self._create_access_path(v) for v in self._obj.values()]
return self.py__iter__list() if isinstance(self._obj, (list, tuple)):
return [self._create_access_path(v) for v in self._obj]
if self.is_instance():
cls = DirectObjectAccess(self._inference_state, self._obj.__class__)
return cls.py__getitem__all_values()
try:
getitem = self._obj.__getitem__
except AttributeError:
pass
else:
annotation = DirectObjectAccess(self._inference_state, getitem).get_return_annotation()
if annotation is not None:
return [annotation]
return None
def py__simple_getitem__(self, index): def py__simple_getitem__(self, index):
if type(self._obj) not in ALLOWED_GETITEM_TYPES: if type(self._obj) not in ALLOWED_GETITEM_TYPES:
@@ -221,8 +236,14 @@ class DirectObjectAccess:
return self._create_access_path(self._obj[index]) return self._create_access_path(self._obj[index])
def py__iter__list(self): def py__iter__list(self):
if not hasattr(self._obj, '__getitem__'): try:
iter_method = self._obj.__iter__
except AttributeError:
return None return None
else:
p = DirectObjectAccess(self._inference_state, iter_method).get_return_annotation()
if p is not None:
return [p]
if type(self._obj) not in ALLOWED_GETITEM_TYPES: if type(self._obj) not in ALLOWED_GETITEM_TYPES:
# Get rid of side effects, we won't call custom `__getitem__`s. # Get rid of side effects, we won't call custom `__getitem__`s.

View File

@@ -166,7 +166,7 @@ class CompiledValue(Value):
except AttributeError: except AttributeError:
return super().py__simple_getitem__(index) return super().py__simple_getitem__(index)
if access is None: if access is None:
return NO_VALUES return super().py__simple_getitem__(index)
return ValueSet([create_from_access_path(self.inference_state, access)]) return ValueSet([create_from_access_path(self.inference_state, access)])

View File

@@ -603,8 +603,11 @@ def test_dict_getitem(code, types):
@pytest.mark.parametrize( @pytest.mark.parametrize(
'code, expected', [ 'code, expected', [
('DunderCls()[0]', 'int'), ('DunderCls()[0]', 'int'),
('dunder[0]', 'int'),
('next(DunderCls())', 'float'), ('next(DunderCls())', 'float'),
('next(dunder)', 'float'),
('for x in DunderCls(): x', 'str'), ('for x in DunderCls(): x', 'str'),
#('for x in dunder: x', 'str'),
] ]
) )
def test_dunders(class_is_findable, code, expected): def test_dunders(class_is_findable, code, expected):
@@ -623,6 +626,8 @@ def test_dunders(class_is_findable, code, expected):
if not class_is_findable: if not class_is_findable:
DunderCls.__name__ = 'asdf' DunderCls.__name__ = 'asdf'
dunder = DunderCls()
n, = jedi.Interpreter(code, [locals()]).infer() n, = jedi.Interpreter(code, [locals()]).infer()
assert n.name == expected assert n.name == expected