diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index ecc6d66b..3f0b9ec8 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -80,7 +80,7 @@ from jedi.evaluate import helpers from jedi.evaluate.filters import TreeNameDefinition, ParamName from jedi.evaluate.instance import AnonymousInstance, BoundMethod from jedi.evaluate.context import ContextualizedName, ContextualizedNode, \ - ContextSet, NO_CONTEXTS + ContextSet, NO_CONTEXTS, iterate_contexts from jedi.evaluate.syntax_tree import eval_trailer, eval_expr_stmt, \ eval_node, check_tuple_assignments from jedi import parser_utils @@ -217,7 +217,7 @@ class Evaluator(object): if type_ == 'for_stmt': container_types = context.eval_node(def_.children[3]) cn = ContextualizedNode(context, def_.children[3]) - for_types = iterable.iterate_contexts(self, container_types, cn) + for_types = iterate_contexts(container_types, cn) c_node = ContextualizedName(context, name) return check_tuple_assignments(self, c_node, for_types) if type_ in ('import_from', 'import_name'): diff --git a/jedi/evaluate/context.py b/jedi/evaluate/context.py index e18474c7..c4fd3fab 100644 --- a/jedi/evaluate/context.py +++ b/jedi/evaluate/context.py @@ -1,7 +1,7 @@ from parso.python.tree import ExprStmt, CompFor from jedi import debug -from jedi._compatibility import Python3Method, zip_longest +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 @@ -66,6 +66,14 @@ class Context(object): return self.evaluator.execute(self, arguments) + def execute_evaluated(self, *value_list): + """ + Execute a function with already executed arguments. + """ + from jedi.evaluate.param import ValuesArguments + arguments = ValuesArguments([ContextSet(value) for value in value_list]) + return self.execute(arguments) + def iterate(self, contextualized_node=None): debug.dbg('iterate') try: @@ -76,19 +84,51 @@ class Context(object): analysis.add( contextualized_node.context, 'type-error-not-iterable', - contextualized_node._node, + contextualized_node.node, message="TypeError: '%s' object is not iterable" % self) return iter([]) else: return iter_method() - def execute_evaluated(self, *value_list): - """ - Execute a function with already executed arguments. - """ - from jedi.evaluate.param import ValuesArguments - arguments = ValuesArguments([ContextSet(value) for value in value_list]) - return self.execute(arguments) + def get_item(self, index_contexts, contextualized_node): + from jedi.evaluate.compiled import CompiledObject + from jedi.evaluate.iterable import Slice, AbstractSequence + result = ContextSet() + + for index in index_contexts: + if isinstance(index, (CompiledObject, Slice)): + index = index.obj + + if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)): + # If the index is not clearly defined, we have to get all the + # possiblities. + if isinstance(self, AbstractSequence) and self.array_type == 'dict': + result |= self.dict_values() + else: + result |= iterate_contexts(ContextSet(self)) + continue + + # The actual getitem call. + try: + getitem = self.py__getitem__ + except AttributeError: + from jedi.evaluate import analysis + # TODO this context is probably not right. + analysis.add( + contextualized_node.context, + 'type-error-not-subscriptable', + contextualized_node.node, + message="TypeError: '%s' object is not subscriptable" % self + ) + else: + try: + result |= getitem(index) + except IndexError: + result |= iterate_contexts(ContextSet(self)) + except KeyError: + # Must be a dict. Lists don't raise KeyErrors. + result |= self.dict_values() + return result def eval_node(self, node): return self.evaluator.eval_element(self, node) @@ -142,6 +182,17 @@ class Context(object): return None +def iterate_contexts(contexts, contextualized_node=None): + """ + Calls `iterate`, on all contexts but ignores the ordering and just returns + all contexts that the iterate functions yield. + """ + return ContextSet.from_sets( + lazy_context.infer() + for lazy_context in contexts.iterate(contextualized_node) + ) + + class TreeContext(Context): def __init__(self, evaluator, parent_context=None): super(TreeContext, self).__init__(evaluator, parent_context) @@ -215,20 +266,20 @@ class MergedLazyContexts(AbstractLazyContext): class ContextualizedNode(object): def __init__(self, context, node): self.context = context - self._node = node + self.node = node def get_root_context(self): return self.context.get_root_context() def infer(self): - return self.context.eval_node(self._node) + return self.context.eval_node(self.node) class ContextualizedName(ContextualizedNode): # TODO merge with TreeNameDefinition?! @property def name(self): - return self._node + return self.node def assignment_indexes(self): """ @@ -242,8 +293,8 @@ class ContextualizedName(ContextualizedNode): would result in ``[(1, xyz_node), (0, yz_node)]``. """ indexes = [] - node = self._node.parent - compare = self._node + node = self.node.parent + compare = self.node while node is not None: if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'): for i, child in enumerate(node.children): diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index be30b6a5..d8730dee 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -613,74 +613,6 @@ def unpack_tuple_to_dict(context, types, exprlist): raise NotImplementedError -def iterate_contexts(evaluator, contexts, contextualized_node=None): - """ - Calls `iterate`, on all contexts but ignores the ordering and just returns - all contexts that the iterate functions yield. - """ - return ContextSet.from_sets( - lazy_context.infer() - for lazy_context in contexts.iterate(contextualized_node) - ) - - -def py__getitem__(evaluator, context, types, trailer): - from jedi.evaluate.representation import ClassContext - from jedi.evaluate.instance import TreeInstance - result = ContextSet() - - trailer_op, node, trailer_cl = trailer.children - assert trailer_op == "[" - assert trailer_cl == "]" - - # TODO It's kind of stupid to cast this from a context set to a set. - types = set(types) - # special case: PEP0484 typing module, see - # https://github.com/davidhalter/jedi/issues/663 - for typ in list(types): - if isinstance(typ, (ClassContext, TreeInstance)): - typing_module_types = pep0484.py__getitem__(context, typ, node) - if typing_module_types is not None: - types.remove(typ) - result |= typing_module_types - - if not types: - # all consumed by special cases - return result - - for index in create_index_types(evaluator, context, node): - if isinstance(index, (compiled.CompiledObject, Slice)): - index = index.obj - - if type(index) not in (float, int, str, unicode, slice, type(Ellipsis)): - # If the index is not clearly defined, we have to get all the - # possiblities. - for typ in list(types): - if isinstance(typ, AbstractSequence) and typ.array_type == 'dict': - types.remove(typ) - result |= typ.dict_values() - cs = ContextSet.from_set(types) - return result | iterate_contexts(evaluator, cs) - - for typ in types: - # The actual getitem call. - try: - getitem = typ.py__getitem__ - except AttributeError: - # TODO this context is probably not right. - analysis.add(context, 'type-error-not-subscriptable', trailer_op, - message="TypeError: '%s' object is not subscriptable" % typ) - else: - try: - result |= getitem(index) - except IndexError: - result |= iterate_contexts(evaluator, ContextSet(typ)) - except KeyError: - # Must be a dict. Lists don't raise KeyErrors. - result |= typ.dict_values() - return result - - def check_array_additions(context, sequence): """ Just a mapper function for the internal _check_array_additions """ if sequence.array_type not in ('list', 'set'): diff --git a/jedi/evaluate/syntax_tree.py b/jedi/evaluate/syntax_tree.py index 2b9ad9d0..1ea6455f 100644 --- a/jedi/evaluate/syntax_tree.py +++ b/jedi/evaluate/syntax_tree.py @@ -10,7 +10,7 @@ from jedi._compatibility import unicode from jedi import debug from jedi import parser_utils from jedi.evaluate.context import ContextSet, NO_CONTEXTS, ContextualizedNode, \ - ContextualizedName, iterator_to_context_set + ContextualizedName, iterator_to_context_set, iterate_contexts from jedi.evaluate import compiled from jedi.evaluate import pep0484 from jedi.evaluate import recursion @@ -119,7 +119,27 @@ def eval_trailer(context, base_contexts, trailer): if trailer_op == '[': from jedi.evaluate import iterable - return iterable.py__getitem__(context.evaluator, context, base_contexts, trailer) + from jedi.evaluate.representation import ClassContext + from jedi.evaluate.instance import TreeInstance + + trailer_op, node, _ = trailer.children + + # TODO It's kind of stupid to cast this from a context set to a set. + foo = set(base_contexts) + # special case: PEP0484 typing module, see + # https://github.com/davidhalter/jedi/issues/663 + result = ContextSet() + for typ in list(foo): + if isinstance(typ, (ClassContext, TreeInstance)): + typing_module_types = pep0484.py__getitem__(context, typ, node) + if typing_module_types is not None: + foo.remove(typ) + result |= typing_module_types + + return result | base_contexts.get_item( + iterable.create_index_types(context.evaluator, context, node), + ContextualizedNode(context, trailer) + ) else: debug.dbg('eval_trailer: %s in %s', trailer, base_contexts) if trailer_op == '.': @@ -475,7 +495,7 @@ def tree_name_to_contexts(evaluator, context, tree_name): types = context.predefined_names[node][tree_name.value] except KeyError: cn = ContextualizedNode(context, node.children[3]) - for_types = iterable.iterate_contexts(evaluator, cn.infer(), cn) + for_types = iterate_contexts(cn.infer(), cn) c_node = ContextualizedName(context, tree_name) types = check_tuple_assignments(evaluator, c_node, for_types) elif typ == 'expr_stmt':