diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index d56fbd60..a1e60c5b 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -197,11 +197,6 @@ class Evaluator(object): return types def eval_element(self, context, element): - if isinstance(element, iterable.AlreadyEvaluated): - return set(element) - elif isinstance(element, iterable.MergedNodes): - return iterable.unite(self.eval_element(context, e) for e in element) - if_stmt = element.get_parent_until((tree.IfStmt, tree.ForStmt, tree.IsScope)) predefined_if_name_dict = self.predefined_if_name_dict_dict.get(if_stmt) if predefined_if_name_dict is None and isinstance(if_stmt, tree.IfStmt): @@ -402,13 +397,13 @@ class Evaluator(object): if trailer_op == '.': new_types |= self.find_types(typ, node) elif trailer_op == '(': - arguments = param.Arguments(self, context, node, trailer) + arguments = param.TreeArguments(self, context, node, trailer) new_types |= self.execute(typ, arguments) return new_types @debug.increase_indent def execute(self, obj, arguments=None): - if not isinstance(arguments, param.Arguments): + if not isinstance(arguments, param.AbstractArguments): raise NotImplementedError arguments = param.Arguments(self, arguments) diff --git a/jedi/evaluate/context.py b/jedi/evaluate/context.py index 0229c2ac..88521a77 100644 --- a/jedi/evaluate/context.py +++ b/jedi/evaluate/context.py @@ -18,15 +18,12 @@ class Context(object): def execute(self, arguments=None): return self._evaluator.execute(self, arguments) - def execute_evaluated(self, *args): + def execute_evaluated(self, *value_list): """ Execute a function with already executed arguments. """ - from jedi.evaluate.iterable import AlreadyEvaluated - from jedi.evaluate.param import Arguments - # TODO UGLY - args = [AlreadyEvaluated([arg]) for arg in args] - return self.execute(Arguments(self._evaluator, self, args)) + from jedi.evaluate.param import ValueArguments + return self.execute(ValueArguments(value_list)) class TreeContext(Context): diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index d0570b59..1d6e868d 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -95,6 +95,8 @@ def search_function_call(evaluator, func): # We have to remove decorators, because they are not the # "original" functions, this way we can easily compare. # At the same time we also have to remove InstanceElements. + return typ + # TODO remove if typ.isinstance(er.Function, er.Instance) \ and typ.decorates is not None: return typ.decorates @@ -129,9 +131,11 @@ def search_function_call(evaluator, func): if i * evaluator.dynamic_params_depth > MAX_PARAM_SEARCHES: return listener.param_possibilities - for typ in evaluator.goto_definitions(name): + context = evaluator.create_context(name) + for typ in evaluator.goto_definitions(context, name): undecorated = undecorate(typ) - if evaluator.wrap(compare) == undecorated: + # TODO really? + if evaluator.wrap(context, compare) == undecorated: # Only if we have the correct function we execute # it, otherwise just ignore it. evaluator.eval_trailer([typ], trailer) diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 41f67ab5..c920f45c 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -25,8 +25,8 @@ class AbstractNameDefinition(): def __repr__(self): if self.start_pos is None: - return '<%s: %s>' % (type(self).__name__, self.string_name) - return '<%s: %s@%s>' % (type(self).__name__, self.string_name, self.start_pos) + return '<%s: %s>' % (self.__class__.__name__, self.string_name) + return '<%s: %s@%s>' % (self.__class__.__name__, self.string_name, self.start_pos) def execute(self, arguments): return unite(context.execute(arguments) for context in self.infer()) diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 4b6a2da9..619d01a5 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -470,7 +470,7 @@ def _eval_param(evaluator, context, param, scope): typ = list(evaluator.find_types(evaluator.BUILTINS, t))[0] res_new = evaluator.execute(typ) if param.default: - res_new |= evaluator.eval_element(param.default) + res_new |= evaluator.eval_element(context, param.default) return res_new diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 8e3280e1..4e6ae34b 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -35,7 +35,7 @@ from jedi.evaluate.filters import DictFilter from jedi.evaluate.context import Context -class AbstractArrayContext(Context): +class AbstractSequence(Context): def get_filters(self, search_global, until_position=None, origin_scope=None): raise NotImplementedError @@ -152,6 +152,7 @@ class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)) return f.get_yield_types() def __getattr__(self, name): + raise NotImplementedError if name not in ['start_pos', 'end_pos', 'parent', 'get_imports', 'doc', 'docstr', 'get_parent_until', 'get_code', 'subscopes']: @@ -288,7 +289,6 @@ class ArrayMixin(object): def _imitate_values(self): items = self.dict_values() return create_evaluated_sequence_set(self._evaluator, items, sequence_type='list') - #return set([FakeSequence(self._evaluator, [AlreadyEvaluated(items)], 'tuple')]) @register_builtin_method('items', type='dict') def _imitate_items(self): @@ -352,7 +352,7 @@ class GeneratorComprehension(Comprehension, GeneratorMixin): pass -class ArrayLiteralContext(AbstractArrayContext, ArrayMixin): +class ArrayLiteralContext(AbstractSequence, ArrayMixin): mapping = {'(': 'tuple', '[': 'list', '{': 'dict'} @@ -390,6 +390,7 @@ class ArrayLiteralContext(AbstractArrayContext, ArrayMixin): return self.parent_context.eval_node(self._items()[index]) def __getattr__(self, name): + raise NotImplementedError if name not in ['start_pos', 'get_only_subelement', 'parent', 'get_parent_until', 'items']: raise AttributeError('Strange access on %s: %s.' % (self, name)) @@ -474,15 +475,28 @@ class ImplicitTuple(_FakeArray): class FakeSequence(_FakeArray): - def __init__(self, evaluator, sequence_values, type): + def __init__(self, evaluator, type, context_sets): """ type should be one of "tuple", "list" """ - super(FakeSequence, self).__init__(evaluator, sequence_values, type) - self._sequence_values = sequence_values + super(FakeSequence, self).__init__(evaluator, context_sets, type) + self._context_sets = context_sets + + def _resolve(self, context_set): + for x in context_set: + try: + infer = x.infer + except AttributeError: + yield x + else: + for value in infer(): + yield value def _items(self): - return self._sequence_values + return self._context_sets + + def py__getitem__(self, index): + return set(self._resolve(self._context_sets[index])) def create_evaluated_sequence_set(evaluator, *types_order, **kwargs): @@ -499,12 +513,17 @@ def create_evaluated_sequence_set(evaluator, *types_order, **kwargs): class AlreadyEvaluated(frozenset): """A simple container to add already evaluated objects to an array.""" + def __init__(self, *args, **kwargs): + raise DeprecationWarning + def get_code(self, normalized=False): # For debugging purposes. return str(self) class MergedNodes(frozenset): + def __init__(self, *args, **kwargs): + raise DeprecationWarning pass diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 58cbeace..056ae9fc 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -8,6 +8,7 @@ from jedi.parser import tree from jedi.evaluate import iterable from jedi.evaluate import analysis from jedi.evaluate import precedence +from jedi.evaluate.context import Context def try_iter_content(types, depth=0): @@ -27,7 +28,66 @@ def try_iter_content(types, depth=0): try_iter_content(iter_types, depth + 1) -class Arguments(tree.Base): +class AbstractArguments(tree.Base): + def get_parent_until(self, *args, **kwargs): + raise DeprecationWarning + if self.trailer is None: + try: + element = self.argument_node[0] + if isinstance(element, iterable.AlreadyEvaluated): + element = list(self._evaluator.eval_element(self._context, element))[0] + except IndexError: + return None + else: + return element.get_parent_until(*args, **kwargs) + else: + return self.trailer.get_parent_until(*args, **kwargs) + + def eval_argument_clinic(self, arguments): + """Uses a list with argument clinic information (see PEP 436).""" + raise DeprecationWarning('not sure if we really deprecate it') + iterator = self.unpack() + for i, (name, optional, allow_kwargs) in enumerate(arguments): + key, va_values = next(iterator, (None, [])) + if key is not None: + raise NotImplementedError + if not va_values and not optional: + debug.warning('TypeError: %s expected at least %s arguments, got %s', + name, len(arguments), i) + raise ValueError + values = set(chain.from_iterable(self._evaluator.eval_element(self._context, el) + for el in va_values)) + if not values and not optional: + # For the stdlib we always want values. If we don't get them, + # that's ok, maybe something is too hard to resolve, however, + # we will not proceed with the evaluation of that function. + debug.warning('argument_clinic "%s" not resolvable.', name) + raise ValueError + yield values + + def scope(self): + raise DeprecationWarning + # Returns the scope in which the arguments are used. + return (self.trailer or self.argument_node).get_parent_until(tree.IsScope) + + def eval_args(self): + # TODO this method doesn't work with named args and a lot of other + # things. Use unpack. + raise DeprecationWarning + return [self._evaluator.eval_element(self._context, el) for stars, el in self._split()] + + def eval_all(self, func=None): + """ + Evaluates all arguments as a support for static analysis + (normally Jedi). + """ + for key, element_values in self.unpack(): + for element in element_values: + types = self._evaluator.eval_element(self._context, element) + try_iter_content(types) + + +class TreeArguments(AbstractArguments): def __init__(self, evaluator, context, argument_node, trailer=None): """ The argument_node is either a parser node or a list of evaluated @@ -66,32 +126,11 @@ class Arguments(tree.Base): else: yield 0, child - def get_parent_until(self, *args, **kwargs): - if self.trailer is None: - try: - element = self.argument_node[0] - from jedi.evaluate.iterable import AlreadyEvaluated - if isinstance(element, AlreadyEvaluated): - element = list(self._evaluator.eval_element(self._context, element))[0] - except IndexError: - return None - else: - return element.get_parent_until(*args, **kwargs) - else: - return self.trailer.get_parent_until(*args, **kwargs) - - def as_tuple(self): - for stars, argument in self._split(): - if tree.is_node(argument, 'argument'): - argument, default = argument.children[::2] - else: - default = None - yield argument, default, stars - def unpack(self, func=None): named_args = [] for stars, el in self._split(): if stars == 1: + raise NotImplementedError arrays = self._evaluator.eval_element(self._context, el) iterators = [_iterate_star_args(self._evaluator, a, el, func) for a in arrays] @@ -99,125 +138,101 @@ class Arguments(tree.Base): for values in list(zip_longest(*iterators)): yield None, [v for v in values if v is not None] elif stars == 2: + raise NotImplementedError arrays = self._evaluator.eval_element(self._context, el) dicts = [_star_star_dict(self._evaluator, a, el, func) for a in arrays] for dct in dicts: for key, values in dct.items(): - yield key, values + yield key, LazyContext(*values) else: if tree.is_node(el, 'argument'): c = el.children if len(c) == 3: # Keyword argument. - named_args.append((c[0].value, (c[2],))) + named_args.append((c[0].value, LazyContext(self._context, c[2]),)) else: # Generator comprehension. # Include the brackets with the parent. comp = iterable.GeneratorComprehension( self._evaluator, self.argument_node.parent) - yield None, (iterable.AlreadyEvaluated([comp]),) - elif isinstance(el, (list, tuple)): - yield None, el + yield None, KnownContext(comp) else: - yield None, (el,) + yield None, LazyContext(self._context, el) # Reordering var_args is necessary, because star args sometimes appear # after named argument, but in the actual order it's prepended. - for key_arg in named_args: - yield key_arg + for named_arg in named_args: + yield named_arg - def _reorder_var_args(var_args): - named_index = None - new_args = [] - for i, stmt in enumerate(var_args): - if isinstance(stmt, tree.ExprStmt): - if named_index is None and stmt.assignment_details: - named_index = i - - if named_index is not None: - expression_list = stmt.expression_list() - if expression_list and expression_list[0] == '*': - new_args.insert(named_index, stmt) - named_index += 1 - continue - - new_args.append(stmt) - return new_args - - def eval_argument_clinic(self, arguments): - """Uses a list with argument clinic information (see PEP 436).""" - iterator = self.unpack() - for i, (name, optional, allow_kwargs) in enumerate(arguments): - key, va_values = next(iterator, (None, [])) - if key is not None: - raise NotImplementedError - if not va_values and not optional: - debug.warning('TypeError: %s expected at least %s arguments, got %s', - name, len(arguments), i) - raise ValueError - values = set(chain.from_iterable(self._evaluator.eval_element(self._context, el) - for el in va_values)) - if not values and not optional: - # For the stdlib we always want values. If we don't get them, - # that's ok, maybe something is too hard to resolve, however, - # we will not proceed with the evaluation of that function. - debug.warning('argument_clinic "%s" not resolvable.', name) - raise ValueError - yield values - - def scope(self): - # Returns the scope in which the arguments are used. - return (self.trailer or self.argument_node).get_parent_until(tree.IsScope) - - def eval_args(self): - # TODO this method doesn't work with named args and a lot of other - # things. Use unpack. - return [self._evaluator.eval_element(self._context, el) for stars, el in self._split()] + def as_tuple(self): + raise DeprecationWarning + for stars, argument in self._split(): + if tree.is_node(argument, 'argument'): + argument, default = argument.children[::2] + else: + default = None + yield argument, default, stars def __repr__(self): return '<%s: %s>' % (type(self).__name__, self.argument_node) def get_calling_var_args(self): - if tree.is_node(self.argument_node, 'arglist', 'argument') \ - or self.argument_node == () and self.trailer is not None: - return _get_calling_var_args(self._evaluator, self) - else: - return None - - def eval_all(self, func=None): - """ - Evaluates all arguments as a support for static analysis - (normally Jedi). - """ - for key, element_values in self.unpack(): - for element in element_values: - types = self._evaluator.eval_element(self._context, element) - try_iter_content(types) + return _get_calling_var_args(self._evaluator, self) -class ExecutedParam(tree.Param): +class KnownContext(object): + def __init__(self, value): + self._value = value + + def infer(self): + return set([self._value]) + + +class UnknownContext(object): + def infer(self): + return set() + + +class LazyContext(object): + def __init__(self, context, node): + self._context = context + self._node = node + + def infer(self): + return self._context.eval_node(self._node) + + +class ValueArguments(AbstractArguments): + def __init__(self, value_list): + self._value_list = value_list + + def unpack(self, func=None): + for value in self._value_list: + yield None, KnownContext(value) + + def get_calling_var_args(self): + return None + + def __repr__(self): + return '<%s: %s>' % (type(self).__name__, self._value_list) + + +class ExecutedParam(object): """Fake a param and give it values.""" - def __init__(self, original_param, var_args, values): + def __init__(self, original_param, var_args, lazy_context): + assert not isinstance(lazy_context, (tuple, list)) self._original_param = original_param self.var_args = var_args - self._values = values + self._lazy_context = lazy_context self.string_name = self._original_param.name.value def infer(self, evaluator): - types = set() - for v in self._values: - # TODO this context selection seems wrong. Fix it once we change - # the way executed params work. - types |= evaluator.eval_element(self.var_args._context, v) - return types + return self._lazy_context.infer() @property def position_nr(self): # Need to use the original logic here, because it uses the parent. return self._original_param.position_nr - def __getattr__(self, name): - return getattr(self._original_param, name) - def _get_calling_var_args(evaluator, var_args): old_var_args = None @@ -244,7 +259,7 @@ def _get_calling_var_args(evaluator, var_args): return var_args.argument_node or var_args.trailer -def get_params(evaluator, func, var_args): +def get_params(evaluator, parent_context, func, var_args): result_params = [] param_dict = {} for param in func.params: @@ -252,8 +267,9 @@ def get_params(evaluator, func, var_args): unpacked_va = list(var_args.unpack(func)) from jedi.evaluate.representation import InstanceElement if isinstance(func, InstanceElement): + raise DeprecationWarning # Include self at this place. - unpacked_va.insert(0, (None, [iterable.AlreadyEvaluated([func.instance])])) + unpacked_va.insert(0, (None, [func.instance])) var_arg_iterator = common.PushBackIterator(iter(unpacked_va)) non_matching_keys = defaultdict(lambda: []) @@ -265,17 +281,17 @@ def get_params(evaluator, func, var_args): # args / kwargs will just be empty arrays / dicts, respectively. # Wrong value count is just ignored. If you try to test cases that are # not allowed in Python, Jedi will maybe not show any completions. - default = [] if param.default is None else [param.default] - key, va_values = next(var_arg_iterator, (None, default)) + default = None if param.default is None else LazyContext(parent_context, param.default) + key, argument = next(var_arg_iterator, (None, default)) while key is not None: keys_only = True k = unicode(key) try: key_param = param_dict[unicode(key)] except KeyError: - non_matching_keys[key] = va_values + non_matching_keys[key] = argument else: - result_params.append(ExecutedParam(key_param, var_args, va_values)) + result_params.append(ExecutedParam(key_param, var_args, argument)) if k in keys_used: had_multiple_value_error = True @@ -291,43 +307,43 @@ def get_params(evaluator, func, var_args): except IndexError: # TODO this is wrong stupid and whatever. pass - key, va_values = next(var_arg_iterator, (None, ())) + key, argument = next(var_arg_iterator, (None, None)) - values = [] if param.stars == 1: # *args param - lst_values = [iterable.MergedNodes(va_values)] if va_values else [] - for key, va_values in var_arg_iterator: - # Iterate until a key argument is found. - if key: - var_arg_iterator.push_back((key, va_values)) - break - if va_values: - lst_values.append(iterable.MergedNodes(va_values)) - seq = iterable.FakeSequence(evaluator, lst_values, 'tuple') - values = [iterable.AlreadyEvaluated([seq])] + values_list = [] + if argument is not None: + values_list.append([argument]) + for key, argument in var_arg_iterator: + # Iterate until a key argument is found. + if key: + var_arg_iterator.push_back((key, argument)) + break + values_list.append([argument]) + seq = iterable.FakeSequence(evaluator, 'tuple', values_list) + result_arg = KnownContext(seq) elif param.stars == 2: # **kwargs param dct = iterable.FakeDict(evaluator, dict(non_matching_keys)) - values = [iterable.AlreadyEvaluated([dct])] + result_arg = KnownContext(dct) non_matching_keys = {} else: # normal param - if va_values: - values = va_values - else: + if argument is not None: # No value: Return an empty container - values = [] + result_arg = UnknownContext() if not keys_only: calling_va = var_args.get_calling_var_args() if calling_va is not None: m = _error_argument_count(func, len(unpacked_va)) analysis.add(evaluator, 'type-error-too-few-arguments', calling_va, message=m) + else: + result_arg = argument # Now add to result if it's not one of the previously covered cases. if (not keys_only or param.stars == 2): - result_params.append(ExecutedParam(param, var_args, values)) + result_params.append(ExecutedParam(param, var_args, result_arg)) keys_used[unicode(param.name)] = result_params[-1] if keys_only: @@ -336,8 +352,8 @@ def get_params(evaluator, func, var_args): # there's nothing to find for certain names. for k in set(param_dict) - set(keys_used): param = param_dict[k] - values = [] if param.default is None else [param.default] - result_params.append(ExecutedParam(param, var_args, values)) + result_arg = UnknownContext() if param.default is None else LazyContext(param.default) + result_params.append(ExecutedParam(param, var_args, result_arg)) if not (non_matching_keys or had_multiple_value_error or param.stars or param.default): @@ -348,11 +364,10 @@ def get_params(evaluator, func, var_args): analysis.add(evaluator, 'type-error-too-few-arguments', calling_va, message=m) - for key, va_values in non_matching_keys.items(): + for key, argument in non_matching_keys.items(): m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (func.name, key) - for value in va_values: - analysis.add(evaluator, 'type-error-keyword-argument', value.parent, message=m) + analysis.add(evaluator, 'type-error-keyword-argument', argument.whatever, message=m) remaining_params = list(var_arg_iterator) if remaining_params: @@ -383,7 +398,7 @@ def get_params(evaluator, func, var_args): def _iterate_star_args(evaluator, array, input_node, func=None): from jedi.evaluate.representation import Instance - if isinstance(array, iterable.Array): + if isinstance(array, iterable.AbstractSequence): # TODO ._items is not the call we want here. Replace in the future. for node in array._items(): yield node diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 98e2d0d5..42d223c9 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -767,7 +767,7 @@ class FunctionExecutionContext(Executed): @memoize_default(default=NO_DEFAULT) def get_params(self): - return param.get_params(self._evaluator, self.funcdef, self.var_args) + return param.get_params(self._evaluator, self.parent_context, self.funcdef, self.var_args) def __repr__(self): return "<%s of %s>" % (type(self).__name__, self.funcdef)