1
0
forked from VimPlug/jedi

Taking a stab at simple *args and generators.

This commit is contained in:
Dave Halter
2016-10-29 02:11:04 +02:00
parent bbb1d1e04c
commit 3cce530ef4
7 changed files with 53 additions and 49 deletions

View File

@@ -25,12 +25,12 @@ class Context(object):
from jedi.evaluate.param import ValueArguments from jedi.evaluate.param import ValueArguments
return self.execute(ValueArguments(value_list)) return self.execute(ValueArguments(value_list))
class TreeContext(Context):
def eval_node(self, node): def eval_node(self, node):
return self._evaluator.eval_element(self, node) return self._evaluator.eval_element(self, node)
class TreeContext(Context):
pass
class FlowContext(TreeContext): class FlowContext(TreeContext):
def get_parent_flow_context(self): def get_parent_flow_context(self):
if 1: if 1:

View File

@@ -68,7 +68,7 @@ class ParamName(ContextName):
self.name = name self.name = name
def infer(self): def infer(self):
return self._get_param().infer(self.parent_context._evaluator) return self._get_param().infer()
def _get_param(self): def _get_param(self):
params = self.parent_context.get_params() params = self.parent_context.get_params()

View File

@@ -360,8 +360,8 @@ def _name_to_types(evaluator, context, name, scope):
types = pep0484.find_type_from_comment_hint_with(evaluator, typ, name) types = pep0484.find_type_from_comment_hint_with(evaluator, typ, name)
if types: if types:
return types return types
if typ.isinstance(tree.ForStmt, tree.CompFor): if typ.type in ('for_stmt', 'comp_for'):
container_types = evaluator.eval_element(typ.children[3]) container_types = context.eval_node(typ.children[3])
for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3]) for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3])
types = check_tuple_assignments(evaluator, for_types, name) types = check_tuple_assignments(evaluator, for_types, name)
elif isinstance(typ, tree.Param): elif isinstance(typ, tree.Param):

View File

@@ -41,10 +41,10 @@ def reachability_check(context, context_scope, node, origin_scope=None):
# e.g. `if 0:` would cause all name lookup within the flow make # e.g. `if 0:` would cause all name lookup within the flow make
# unaccessible. This is not a "problem" in Python, because the code is # unaccessible. This is not a "problem" in Python, because the code is
# never called. In Jedi though, we still want to infer types. # never called. In Jedi though, we still want to infer types.
while origin_scope is not None: #while origin_scope is not None:
if flow_scope == origin_scope: #if flow_scope == origin_scope:
return REACHABLE #return REACHABLE
origin_scope = origin_scope.parent #origin_scope = origin_scope.parent
return _break_check(context, context_scope, flow_scope, node) return _break_check(context, context_scope, flow_scope, node)

View File

@@ -41,9 +41,7 @@ class AbstractSequence(Context):
@property @property
def name(self): def name(self):
raise NotImplementedError return compiled.CompiledContextName(self, self.type)
#return compiled.CompiledContextName(
return helpers.FakeName(self.type, parent=self)
class IterableWrapper(tree.Base): class IterableWrapper(tree.Base):
@@ -137,19 +135,15 @@ class GeneratorMixin(object):
return gen_obj.py__class__() return gen_obj.py__class__()
class Generator(use_metaclass(CachedMetaClass, IterableWrapper, GeneratorMixin)): class Generator(Context, GeneratorMixin):
"""Handling of `yield` functions.""" """Handling of `yield` functions."""
def __init__(self, evaluator, func, var_args): def __init__(self, evaluator, func_execution_context):
super(Generator, self).__init__() super(Generator, self).__init__(evaluator)
self._evaluator = evaluator self._func_execution_context = func_execution_context
self.func = func
self.var_args = var_args
def py__iter__(self): def py__iter__(self):
from jedi.evaluate.representation import FunctionExecution return self._func_execution_context.get_yield_values()
f = FunctionExecution(self._evaluator, self.func, self.var_args)
return f.get_yield_types()
def __getattr__(self, name): def __getattr__(self, name):
raise NotImplementedError raise NotImplementedError
@@ -389,13 +383,6 @@ class ArrayLiteralContext(AbstractSequence, ArrayMixin):
else: else:
return self.parent_context.eval_node(self._items()[index]) 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))
return getattr(self.atom, name)
# @memoize_default() # @memoize_default()
def py__iter__(self): def py__iter__(self):
""" """
@@ -413,7 +400,7 @@ class ArrayLiteralContext(AbstractSequence, ArrayMixin):
yield types yield types
else: else:
for value in self._items(): for value in self._items():
yield self._evaluator.eval_element(value) yield self.parent_context.eval_node(value)
additions = check_array_additions(self._evaluator, self) additions = check_array_additions(self._evaluator, self)
if additions: if additions:

View File

@@ -8,7 +8,6 @@ from jedi.parser import tree
from jedi.evaluate import iterable from jedi.evaluate import iterable
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate import precedence from jedi.evaluate import precedence
from jedi.evaluate.context import Context
def try_iter_content(types, depth=0): def try_iter_content(types, depth=0):
@@ -130,21 +129,19 @@ class TreeArguments(AbstractArguments):
named_args = [] named_args = []
for stars, el in self._split(): for stars, el in self._split():
if stars == 1: if stars == 1:
raise NotImplementedError
arrays = self._evaluator.eval_element(self._context, el) arrays = self._evaluator.eval_element(self._context, el)
iterators = [_iterate_star_args(self._evaluator, a, el, func) iterators = [_iterate_star_args(self._evaluator, a, el, func)
for a in arrays] for a in arrays]
iterators = list(iterators) iterators = list(iterators)
for values in list(zip_longest(*iterators)): for values in list(zip_longest(*iterators)):
yield None, [v for v in values if v is not None] yield None, MergedLazyContexts(values)
elif stars == 2: elif stars == 2:
raise NotImplementedError
arrays = self._evaluator.eval_element(self._context, el) arrays = self._evaluator.eval_element(self._context, el)
dicts = [_star_star_dict(self._evaluator, a, el, func) dicts = [_star_star_dict(self._evaluator, a, el, func)
for a in arrays] for a in arrays]
for dct in dicts: for dct in dicts:
for key, values in dct.items(): for key, values in dct.items():
yield key, LazyContext(*values) yield key, values
else: else:
if tree.is_node(el, 'argument'): if tree.is_node(el, 'argument'):
c = el.children c = el.children
@@ -187,6 +184,14 @@ class KnownContext(object):
return set([self._value]) return set([self._value])
class KnownContexts(object):
def __init__(self, values):
self._values = values
def infer(self):
return self._values
class UnknownContext(object): class UnknownContext(object):
def infer(self): def infer(self):
return set() return set()
@@ -201,6 +206,14 @@ class LazyContext(object):
return self._context.eval_node(self._node) return self._context.eval_node(self._node)
class MergedLazyContexts(object):
def __init__(self, lazy_contexts):
self._lazy_contexts = lazy_contexts
def infer(self):
return common.unite(l.infer() for l in self._lazy_contexts)
class ValueArguments(AbstractArguments): class ValueArguments(AbstractArguments):
def __init__(self, value_list): def __init__(self, value_list):
self._value_list = value_list self._value_list = value_list
@@ -225,7 +238,7 @@ class ExecutedParam(object):
self._lazy_context = lazy_context self._lazy_context = lazy_context
self.string_name = self._original_param.name.value self.string_name = self._original_param.name.value
def infer(self, evaluator): def infer(self):
return self._lazy_context.infer() return self._lazy_context.infer()
@property @property
@@ -238,6 +251,7 @@ def _get_calling_var_args(evaluator, var_args):
old_var_args = None old_var_args = None
while var_args != old_var_args: while var_args != old_var_args:
old_var_args = var_args old_var_args = var_args
continue#TODO REMOVE
for name, default, stars in reversed(list(var_args.as_tuple())): for name, default, stars in reversed(list(var_args.as_tuple())):
if not stars or not isinstance(name, tree.Name): if not stars or not isinstance(name, tree.Name):
continue continue
@@ -329,7 +343,7 @@ def get_params(evaluator, parent_context, func, var_args):
non_matching_keys = {} non_matching_keys = {}
else: else:
# normal param # normal param
if argument is not None: if argument is None:
# No value: Return an empty container # No value: Return an empty container
result_arg = UnknownContext() result_arg = UnknownContext()
if not keys_only: if not keys_only:
@@ -352,7 +366,8 @@ def get_params(evaluator, parent_context, func, var_args):
# there's nothing to find for certain names. # there's nothing to find for certain names.
for k in set(param_dict) - set(keys_used): for k in set(param_dict) - set(keys_used):
param = param_dict[k] param = param_dict[k]
result_arg = UnknownContext() if param.default is None else LazyContext(param.default) result_arg = (UnknownContext() if param.default is None else
LazyContext(parent_context, param.default))
result_params.append(ExecutedParam(param, var_args, result_arg)) result_params.append(ExecutedParam(param, var_args, result_arg))
if not (non_matching_keys or had_multiple_value_error or if not (non_matching_keys or had_multiple_value_error or
@@ -399,12 +414,13 @@ def get_params(evaluator, parent_context, func, var_args):
def _iterate_star_args(evaluator, array, input_node, func=None): def _iterate_star_args(evaluator, array, input_node, func=None):
from jedi.evaluate.representation import Instance from jedi.evaluate.representation import Instance
if isinstance(array, iterable.AbstractSequence): if isinstance(array, iterable.AbstractSequence):
raise DeprecationWarning('_items? seriously?')
# TODO ._items is not the call we want here. Replace in the future. # TODO ._items is not the call we want here. Replace in the future.
for node in array._items(): for node in array._items():
yield node yield node
elif isinstance(array, iterable.Generator): elif isinstance(array, iterable.Generator):
for types in array.py__iter__(): for types in array.py__iter__():
yield iterable.AlreadyEvaluated(types) yield KnownContexts(types)
elif isinstance(array, Instance) and array.name.get_code() == 'tuple': elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
debug.warning('Ignored a tuple *args input %s' % array) debug.warning('Ignored a tuple *args input %s' % array)
else: else:

View File

@@ -603,15 +603,16 @@ class FunctionContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
@Python3Method @Python3Method
def py__call__(self, params): def py__call__(self, params):
if self.base.is_generator(): function_execution = FunctionExecutionContext(
return set([iterable.Generator(self._evaluator, self, params)])
else:
return FunctionExecutionContext(
self._evaluator, self._evaluator,
self.parent_context, self.parent_context,
self.base, self.base,
params params
).get_return_types() )
if self.base.is_generator():
return set([iterable.Generator(self._evaluator, function_execution)])
else:
return function_execution.get_return_values()
def py__class__(self): def py__class__(self):
# This differentiation is only necessary for Python2. Python3 does not # This differentiation is only necessary for Python2. Python3 does not
@@ -663,7 +664,7 @@ class FunctionExecutionContext(Executed):
@memoize_default(default=set()) @memoize_default(default=set())
@recursion.execution_recursion_decorator @recursion.execution_recursion_decorator
def get_return_types(self, check_yields=False): def get_return_values(self, check_yields=False):
funcdef = self.funcdef funcdef = self.funcdef
if funcdef.type in ('lambdef', 'lambdef_nocond'): if funcdef.type in ('lambdef', 'lambdef_nocond'):
return self._evaluator.eval_element(self.children[-1]) return self._evaluator.eval_element(self.children[-1])
@@ -713,8 +714,8 @@ class FunctionExecutionContext(Executed):
yield self.eval_node(node) yield self.eval_node(node)
@recursion.execution_recursion_decorator @recursion.execution_recursion_decorator
def get_yield_types(self): def get_yield_values(self):
yields = self.yields yields = self.funcdef.yields
stopAt = tree.ForStmt, tree.WhileStmt, tree.IfStmt stopAt = tree.ForStmt, tree.WhileStmt, tree.IfStmt
for_parents = [(x, x.get_parent_until((stopAt))) for x in yields] for_parents = [(x, x.get_parent_until((stopAt))) for x in yields]
@@ -736,7 +737,7 @@ class FunctionExecutionContext(Executed):
elif for_stmt == self: elif for_stmt == self:
yields_order.append((None, [yield_])) yields_order.append((None, [yield_]))
else: else:
yield self.get_return_types(check_yields=True) yield self.get_return_values(check_yields=True)
return return
last_for_stmt = for_stmt last_for_stmt = for_stmt