From fe78fa9850404147dc17a23cd727a257cf9d1eb4 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Mon, 13 Aug 2018 18:42:09 +0200 Subject: [PATCH] Move to using py__getitem__ and py__simple_getitem__ This change is necessary to handle more complex cases with py__getitem__ --- jedi/evaluate/__init__.py | 3 -- jedi/evaluate/base_context.py | 60 ++++++++++++++----------------- jedi/evaluate/compiled/access.py | 8 +++-- jedi/evaluate/compiled/context.py | 17 ++++----- jedi/evaluate/context/iterable.py | 49 ++++++++++++------------- jedi/evaluate/helpers.py | 15 ++------ test/completion/arrays.py | 2 +- 7 files changed, 68 insertions(+), 86 deletions(-) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 7e1ba08e..78aea1e1 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -67,7 +67,6 @@ from parso.python import tree import parso from parso import python_bytes_to_unicode -from jedi._compatibility import unicode from jedi import debug from jedi import parser_utils from jedi.evaluate.utils import unite @@ -84,8 +83,6 @@ from jedi.evaluate.context import ClassContext, FunctionContext, \ from jedi.evaluate.context.iterable import CompForContext from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \ eval_node, check_tuple_assignments -from jedi.evaluate.helpers import EvaluatorIndexError, EvaluatorKeyError, \ - EvaluatorTypeError def _execute(context, arguments): diff --git a/jedi/evaluate/base_context.py b/jedi/evaluate/base_context.py index a45b487f..a0a00dff 100644 --- a/jedi/evaluate/base_context.py +++ b/jedi/evaluate/base_context.py @@ -12,8 +12,7 @@ from jedi import debug from jedi._compatibility import Python3Method, zip_longest, unicode from jedi.parser_utils import clean_scope_docstring, get_doc_with_call_signature from jedi.common import BaseContextSet, BaseContext -from jedi.evaluate.helpers import EvaluatorIndexError, EvaluatorTypeError, \ - EvaluatorKeyError, execute_evaluated +from jedi.evaluate.helpers import SimpleGetItemNotFound, execute_evaluated class Context(BaseContext): @@ -172,9 +171,10 @@ def _get_item(context, index_contexts, contextualized_node): from jedi.evaluate.context.iterable import Slice, Sequence # The actual getitem call. - try: - getitem = context.py__simple_getitem__ - except AttributeError: + simple_getitem = getattr(context, 'py__simple_getitem__', None) + getitem = getattr(context, 'py__getitem__', None) + + if getitem is None and simple_getitem is None: from jedi.evaluate import analysis # TODO this context is probably not right. analysis.add( @@ -186,35 +186,29 @@ def _get_item(context, index_contexts, contextualized_node): return NO_CONTEXTS result = ContextSet() - for index in index_contexts: - if isinstance(index, Slice): - index = index.obj - if isinstance(index, CompiledObject): - try: - index = index.get_safe_value() - except ValueError: - pass + for index_context in index_contexts: + if simple_getitem is not None: + index = index_context + if isinstance(index_context, Slice): + index = index.obj + if isinstance(index, CompiledObject): + try: + index = index.get_safe_value() + except ValueError: + pass - if type(index) not in (float, int, str, unicode, slice, bytes): - # If the index is not clearly defined, we have to get all the - # possiblities. - if isinstance(context, Sequence) and context.array_type == 'dict': - result |= context.dict_values() - else: - result |= iterate_contexts(ContextSet(context)) - continue - else: - try: - result |= getitem(index) - except EvaluatorIndexError: - result |= iterate_contexts(ContextSet(context)) - except EvaluatorKeyError: - # Must be a dict. Lists don't raise KeyErrors. - result |= context.dict_values() - except EvaluatorTypeError: - # The type is wrong and therefore it makes no sense to do - # anything anymore. - result = NO_CONTEXTS + if type(index) in (float, int, str, unicode, slice, bytes): + try: + result |= simple_getitem(index) + continue + except SimpleGetItemNotFound: + pass + + # The index was somehow not good enough or simply a wrong type. + # Therefore we now iterate through all the contexts and just take + # all results. + if getitem is not None: + result |= getitem(index_context, contextualized_node) return result diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index 25b624f7..9fbdbb0c 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -226,6 +226,11 @@ class DirectObjectAccess(object): def py__mro__accesses(self): return tuple(self._create_access_path(cls) for cls in self._obj.__mro__[1:]) + def py__getitem__all_values(self): + if isinstance(self._obj, dict): + return [self._create_access_path(v) for v in self._obj.values()] + return self.py__iter__list() + def py__simple_getitem__(self, index): if type(self._obj) not in (str, list, tuple, unicode, bytes, bytearray, dict): # Get rid of side effects, we won't call custom `__getitem__`s. @@ -416,9 +421,6 @@ class DirectObjectAccess(object): def negate(self): return self._create_access_path(-self._obj) - def dict_values(self): - return [self._create_access_path(v) for v in self._obj.values()] - def is_super_class(self, exception): return issubclass(exception, self._obj) diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index 5c610895..728f9d2b 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -13,7 +13,7 @@ from jedi.evaluate.base_context import Context, ContextSet from jedi.evaluate.lazy_context import LazyKnownContext from jedi.evaluate.compiled.access import _sentinel from jedi.evaluate.cache import evaluator_function_cache -from jedi.evaluate.helpers import reraise_as_evaluator, execute_evaluated +from jedi.evaluate.helpers import reraise_getitem_errors, execute_evaluated from . import fake @@ -155,13 +155,20 @@ class CompiledObject(Context): @CheckAttribute(u'__getitem__') def py__simple_getitem__(self, index): - with reraise_as_evaluator(IndexError, KeyError, TypeError): + with reraise_getitem_errors(IndexError, KeyError, TypeError): access = self.access_handle.py__simple_getitem__(index) if access is None: return ContextSet() return ContextSet(create_from_access_path(self.evaluator, access)) + @CheckAttribute() + def py__getitem__(self, index_context, contextualized_node): + return ContextSet.from_iterable( + create_from_access_path(self.evaluator, access) + for access in self.access_handle.py__getitem__all_values() + ) + @CheckAttribute() def py__iter__(self): for access in self.access_handle.py__iter__list(): @@ -197,12 +204,6 @@ class CompiledObject(Context): for type_ in docstrings.infer_return_types(self): yield type_ - def dict_values(self): - return ContextSet.from_iterable( - create_from_access_path(self.evaluator, access) - for access in self.access_handle.dict_values() - ) - def get_safe_value(self, default=_sentinel): try: return self.access_handle.get_safe_value() diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index 13ff9451..fb6aa668 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -30,8 +30,8 @@ from jedi.evaluate import recursion from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts, \ LazyTreeContext from jedi.evaluate.helpers import get_int_or_none, is_string, \ - predefine_names, evaluate_call_of_leaf, reraise_as_evaluator, \ - EvaluatorKeyError + predefine_names, evaluate_call_of_leaf, reraise_getitem_errors, \ + SimpleGetItemNotFound from jedi.evaluate.utils import safe_property from jedi.evaluate.utils import to_list from jedi.evaluate.cache import evaluator_method_cache @@ -39,7 +39,7 @@ from jedi.evaluate.helpers import execute_evaluated from jedi.evaluate.filters import ParserTreeFilter, BuiltinOverwrite, \ publish_method from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS, Context, \ - TreeContext, ContextualizedNode + TreeContext, ContextualizedNode, iterate_contexts from jedi.parser_utils import get_comp_fors @@ -202,11 +202,10 @@ class Sequence(BuiltinOverwrite, IterableMixin): def parent(self): return self.evaluator.builtins_module - def dict_values(self): - return ContextSet.from_sets( - self._defining_context.eval_node(v) - for k, v in self._items() - ) + def py__getitem__(self, index_context, contextualized_node): + if self.array_type == 'dict': + return self._dict_values() + return iterate_contexts(ContextSet(self)) class ListComprehension(ComprehensionMixin, Sequence): @@ -217,7 +216,7 @@ class ListComprehension(ComprehensionMixin, Sequence): return ContextSet(self) all_types = list(self.py__iter__()) - with reraise_as_evaluator(IndexError, TypeError): + with reraise_getitem_errors(IndexError, TypeError): lazy_context = all_types[index] return lazy_context.infer() @@ -242,14 +241,14 @@ class DictComprehension(ComprehensionMixin, Sequence): if isinstance(k, compiled.CompiledObject): if k.get_safe_value(default=object()) == index: return values - return self.dict_values() + raise SimpleGetItemNotFound() - def dict_values(self): + def _dict_values(self): return ContextSet.from_sets(values for keys, values in self._iterate()) @publish_method('values') def _imitate_values(self): - lazy_context = LazyKnownContexts(self.dict_values()) + lazy_context = LazyKnownContexts(self._dict_values()) return ContextSet(FakeSequence(self.evaluator, u'list', [lazy_context])) @publish_method('items') @@ -298,13 +297,12 @@ class SequenceLiteralContext(Sequence): if isinstance(k, compiled.CompiledObject) \ and k.execute_operation(compiled_obj_index, u'==').get_safe_value(): return self._defining_context.eval_node(value) - raise EvaluatorKeyError('No key found in dictionary %s.' % self) + raise SimpleGetItemNotFound('No key found in dictionary %s.' % self) - # Can raise an IndexError if isinstance(index, slice): return ContextSet(self) else: - with reraise_as_evaluator(TypeError, KeyError, IndexError): + with reraise_getitem_errors(TypeError, KeyError, IndexError): node = self._items()[index] return self._defining_context.eval_node(node) @@ -329,12 +327,11 @@ class SequenceLiteralContext(Sequence): for addition in check_array_additions(self._defining_context, self): yield addition - def _values(self): - """Returns a list of a list of node.""" - if self.array_type == u'dict': - return ContextSet.from_sets(v for k, v in self._items()) - else: - return self._items() + def _dict_values(self): + return ContextSet.from_sets( + self._defining_context.eval_node(v) + for k, v in self._items() + ) def _items(self): c = self.atom.children @@ -387,7 +384,7 @@ class DictLiteralContext(SequenceLiteralContext): @publish_method('values') def _imitate_values(self): - lazy_context = LazyKnownContexts(self.dict_values()) + lazy_context = LazyKnownContexts(self._dict_values()) return ContextSet(FakeSequence(self.evaluator, u'list', [lazy_context])) @publish_method('items') @@ -420,7 +417,7 @@ class FakeSequence(_FakeArray): self._lazy_context_list = lazy_context_list def py__simple_getitem__(self, index): - with reraise_as_evaluator(IndexError, TypeError): + with reraise_getitem_errors(IndexError, TypeError): lazy_context = self._lazy_context_list[index] return lazy_context.infer() @@ -459,7 +456,7 @@ class FakeDict(_FakeArray): except KeyError: pass - with reraise_as_evaluator(KeyError): + with reraise_getitem_errors(KeyError): lazy_context = self._dct[index] return lazy_context.infer() @@ -467,10 +464,10 @@ class FakeDict(_FakeArray): def _values(self): return ContextSet(FakeSequence( self.evaluator, u'tuple', - [LazyKnownContexts(self.dict_values())] + [LazyKnownContexts(self._dict_values())] )) - def dict_values(self): + def _dict_values(self): return ContextSet.from_sets(lazy_context.infer() for lazy_context in self._dct.values()) def exact_key_items(self): diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index 088f4418..bee06dcd 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -218,25 +218,16 @@ def is_number(context): return _get_safe_value_or_none(context, (int, float)) is not None -class EvaluatorTypeError(Exception): - pass - - -class EvaluatorIndexError(Exception): - pass - - -class EvaluatorKeyError(Exception): +class SimpleGetItemNotFound(Exception): pass @contextmanager -def reraise_as_evaluator(*exception_classes): +def reraise_getitem_errors(*exception_classes): try: yield except exception_classes as e: - new_exc_cls = globals()['Evaluator' + e.__class__.__name__] - raise new_exc_cls(e) + raise SimpleGetItemNotFound(e) def execute_evaluated(context, *value_list): diff --git a/test/completion/arrays.py b/test/completion/arrays.py index ffbf2337..6fa6ba05 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -30,7 +30,7 @@ b = [6,7] #? int() b[8-7] # Something unreasonable: -#? +#? int() b[''] # -----------------