diff --git a/jedi/evaluate/base_context.py b/jedi/evaluate/base_context.py index 2c6fe6cd..3f0a198c 100644 --- a/jedi/evaluate/base_context.py +++ b/jedi/evaluate/base_context.py @@ -12,6 +12,8 @@ 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 class Context(BaseContext): @@ -128,11 +130,15 @@ class Context(BaseContext): else: try: result |= getitem(index) - except IndexError: + except EvaluatorIndexError: result |= iterate_contexts(ContextSet(self)) - except KeyError: + except EvaluatorKeyError: # Must be a dict. Lists don't raise KeyErrors. result |= self.dict_values() + except EvaluatorTypeError: + # The type is wrong and therefore it makes no sense to do + # anything anymore. + result = NO_CONTEXTS return result def eval_node(self, node): diff --git a/jedi/evaluate/compiled/context.py b/jedi/evaluate/compiled/context.py index f81509d7..a51717f0 100644 --- a/jedi/evaluate/compiled/context.py +++ b/jedi/evaluate/compiled/context.py @@ -13,6 +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 from . import fake @@ -145,7 +146,8 @@ class CompiledObject(Context): @CheckAttribute def py__getitem__(self, index): - access = self.access_handle.py__getitem__(index) + with reraise_as_evaluator(IndexError, KeyError, TypeError): + access = self.access_handle.py__getitem__(index) if access is None: return ContextSet() diff --git a/jedi/evaluate/context/iterable.py b/jedi/evaluate/context/iterable.py index df012f50..100b8da0 100644 --- a/jedi/evaluate/context/iterable.py +++ b/jedi/evaluate/context/iterable.py @@ -30,7 +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 + predefine_names, evaluate_call_of_leaf, reraise_as_evaluator, \ + EvaluatorKeyError from jedi.evaluate.utils import safe_property from jedi.evaluate.utils import to_list from jedi.evaluate.cache import evaluator_method_cache @@ -219,7 +220,9 @@ class ListComprehension(ComprehensionMixin, Sequence): return ContextSet(self) all_types = list(self.py__iter__()) - return all_types[index].infer() + with reraise_as_evaluator(IndexError, TypeError): + lazy_context = all_types[index] + return lazy_context.infer() class SetComprehension(ComprehensionMixin, Sequence): @@ -293,13 +296,15 @@ 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 KeyError('No key found in dictionary %s.' % self) + raise EvaluatorKeyError('No key found in dictionary %s.' % self) # Can raise an IndexError if isinstance(index, slice): return ContextSet(self) else: - return self._defining_context.eval_node(self._items()[index]) + with reraise_as_evaluator(TypeError, KeyError, IndexError): + node = self._items()[index] + return self._defining_context.eval_node(node) def py__iter__(self): """ @@ -413,7 +418,9 @@ class FakeSequence(_FakeArray): self._lazy_context_list = lazy_context_list def py__getitem__(self, index): - return self._lazy_context_list[index].infer() + with reraise_as_evaluator(IndexError, TypeError): + lazy_context = self._lazy_context_list[index] + return lazy_context.infer() def py__iter__(self): return self._lazy_context_list @@ -450,7 +457,9 @@ class FakeDict(_FakeArray): except KeyError: pass - return self._dct[index].infer() + with reraise_as_evaluator(KeyError): + lazy_context = self._dct[index] + return lazy_context.infer() @publish_method('values') def _values(self): diff --git a/jedi/evaluate/helpers.py b/jedi/evaluate/helpers.py index c6226cde..d1c8b64a 100644 --- a/jedi/evaluate/helpers.py +++ b/jedi/evaluate/helpers.py @@ -9,7 +9,6 @@ from parso.python import tree from jedi._compatibility import unicode from jedi.parser_utils import get_parent_scope -from jedi.evaluate.compiled import CompiledObject def is_stdlib_path(path): @@ -184,6 +183,7 @@ def predefine_names(context, flow_scope, dct): def is_compiled(context): + from jedi.evaluate.compiled import CompiledObject return isinstance(context, CompiledObject) @@ -212,3 +212,24 @@ def get_int_or_none(context): 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): + pass + + +@contextmanager +def reraise_as_evaluator(*exception_classes): + try: + yield + except exception_classes as e: + new_exc_cls = globals()['Evaluator' + e.__class__.__name__] + raise new_exc_cls(e) diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 63eb8c2f..302349d3 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -29,6 +29,9 @@ b = [6,7] #? int() b[8-7] +# Something unreasonable: +#? +b[''] # ----------------- # Slices