1
0
forked from VimPlug/jedi

Fixed all generator tests.

This commit is contained in:
Dave Halter
2016-11-14 20:57:46 +01:00
parent 65d3e29146
commit 03aa630932
5 changed files with 83 additions and 67 deletions

View File

@@ -199,14 +199,17 @@ class DictFilter(AbstractFilter):
def get(self, name): def get(self, name):
try: try:
leaf_name = self._dct[str(name)] value = self._convert(name, self._dct[str(name)])
except KeyError: except KeyError:
return [] return []
return list(self._filter([leaf_name])) return list(self._filter([value]))
def values(self): def values(self):
return self._filter(self._dct.values()) return self._filter(self._convert(*item) for item in self._dct.items())
def _convert(self, name, value):
return value
def get_global_filters(evaluator, context, until_position, origin_scope): def get_global_filters(evaluator, context, until_position, origin_scope):

View File

@@ -5,7 +5,7 @@ from jedi.common import unite
from jedi import debug from jedi import debug
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate import filters from jedi.evaluate import filters
from jedi.evaluate.context import Context, LazyKnownContext, get_merged_lazy_context from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts
from jedi.evaluate.cache import memoize_default from jedi.evaluate.cache import memoize_default
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.evaluate import representation as er from jedi.evaluate import representation as er
@@ -127,11 +127,12 @@ class AbstractInstanceContext(Context):
if isinstance(generator, AbstractInstanceContext): if isinstance(generator, AbstractInstanceContext):
# `__next__` logic. # `__next__` logic.
name = '__next__' if is_py3 else 'next' name = '__next__' if is_py3 else 'next'
try: iter_slot_names = generator.get_function_slot_names(name)
yield get_merged_lazy_context( if iter_slot_names:
generator.execute_subscope_by_name(name) yield LazyKnownContexts(
generator.execute_function_slots(iter_slot_names)
) )
except KeyError: else:
debug.warning('Instance has no __next__ function in %s.', generator) debug.warning('Instance has no __next__ function in %s.', generator)
else: else:
for lazy_context in generator.py__iter__(): for lazy_context in generator.py__iter__():

View File

@@ -23,15 +23,15 @@ It is important to note that:
from jedi.common import unite, safe_property from jedi.common import unite, safe_property
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
from jedi._compatibility import use_metaclass, unicode, zip_longest from jedi._compatibility import unicode, zip_longest, is_py3
from jedi.parser import tree from jedi.parser import tree
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate.cache import CachedMetaClass, memoize_default from jedi.evaluate.cache import memoize_default
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate import pep0484 from jedi.evaluate import pep0484
from jedi import common from jedi import common
from jedi.evaluate.filters import DictFilter from jedi.evaluate.filters import DictFilter, AbstractNameDefinition
from jedi.evaluate import context from jedi.evaluate import context
from jedi.evaluate import precedence from jedi.evaluate import precedence
@@ -45,51 +45,55 @@ class AbstractSequence(context.Context):
return compiled.CompiledContextName(self, self.array_type) return compiled.CompiledContextName(self, self.array_type)
class IterableWrapper(tree.Base): class BuiltinMethod(object):
def is_class(self):
return False
@memoize_default()
def _get_names_dict(self, names_dict):
raise NotImplementedError
builtin_methods = {}
for cls in reversed(type(self).mro()):
try:
builtin_methods.update(cls.builtin_methods)
except AttributeError:
pass
if not builtin_methods:
return names_dict
dct = {}
for names in names_dict.values():
for name in names:
name_str = name.value
try:
method = builtin_methods[name_str, self.type]
except KeyError:
dct[name_str] = name
else:
parent = BuiltinMethod(self, method, name.parent)
dct[name_str] = helpers.FakeName(name_str, parent, is_definition=True)
return dct
class BuiltinMethod(IterableWrapper):
"""``Generator.__next__`` ``dict.values`` methods and so on.""" """``Generator.__next__`` ``dict.values`` methods and so on."""
def __init__(self, builtin, method, builtin_func): def __init__(self, builtin_context, method, builtin_func):
self._builtin = builtin self._builtin_context = builtin_context
self._method = method self._method = method
self._builtin_func = builtin_func self._builtin_func = builtin_func
def py__call__(self, params): def py__call__(self, params):
return self._method(self._builtin) return self._method(self._builtin_context)
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self._builtin_func, name) return getattr(self._builtin_func, name)
class SpecialMethodFilter(DictFilter):
"""
A filter for methods that are defined in this module on the corresponding
classes like Generator (for __next__, etc).
"""
class SpecialMethodName(AbstractNameDefinition):
def __init__(self, parent_context, string_name, callable_, builtin_context):
self.parent_context = parent_context
self.string_name = string_name
self._callable = callable_
self._builtin_context = builtin_context
def infer(self):
filter = next(self._builtin_context.get_filters())
# We can take the first index, because on builtin methods there's
# always only going to be one name. The same is true for the
# inferred values.
builtin_func = filter.get(self.string_name)[0].infer().pop()
return set([BuiltinMethod(self.parent_context, self._callable, builtin_func)])
def __init__(self, context, dct, builtin_context):
super(SpecialMethodFilter, self).__init__(dct)
self.context = context
self._builtin_context = builtin_context
"""
This context is what will be used to introspect the name, where as the
other context will be used to execute the function.
We distinguish, because we have to.
"""
def _convert(self, name, value):
return self.SpecialMethodName(self.context, name, value, self._builtin_context)
def has_builtin_methods(cls): def has_builtin_methods(cls):
cls.builtin_methods = {} cls.builtin_methods = {}
for func in cls.__dict__.values(): for func in cls.__dict__.values():
@@ -100,10 +104,13 @@ def has_builtin_methods(cls):
return cls return cls
def register_builtin_method(method_name, type=None): def register_builtin_method(method_name, python_version_match=None):
def wrapper(func): def wrapper(func):
if python_version_match and python_version_match + int(is_py3) == 3:
# Some functions do only apply to certain versions.
return func
dct = func.__dict__.setdefault('registered_builtin_methods', {}) dct = func.__dict__.setdefault('registered_builtin_methods', {})
dct[method_name, type] = func dct[method_name] = func
return func return func
return wrapper return wrapper
@@ -113,11 +120,11 @@ class GeneratorMixin(object):
type = None type = None
@register_builtin_method('send') @register_builtin_method('send')
@register_builtin_method('next') @register_builtin_method('next', python_version_match=2)
@register_builtin_method('__next__') @register_builtin_method('__next__', python_version_match=3)
def py__next__(self): def py__next__(self):
# TODO add TypeError if params are given. # TODO add TypeError if params are given.
return unite(self.py__iter__()) return unite(lazy_context.infer() for lazy_context in self.py__iter__())
@memoize_default() @memoize_default()
def names_dicts(self, search_global=False): # is always False def names_dicts(self, search_global=False): # is always False
@@ -126,7 +133,9 @@ class GeneratorMixin(object):
def get_filters(self, search_global, until_position=None, origin_scope=None): def get_filters(self, search_global, until_position=None, origin_scope=None):
gen_obj = compiled.get_special_object(self.evaluator, 'GENERATOR_OBJECT') gen_obj = compiled.get_special_object(self.evaluator, 'GENERATOR_OBJECT')
yield DictFilter(self._get_names_dict(gen_obj.names_dict)) yield SpecialMethodFilter(self, self.builtin_methods, gen_obj)
for filter in gen_obj.get_filters(search_global):
yield filter
def py__bool__(self): def py__bool__(self):
return True return True
@@ -150,7 +159,7 @@ class Generator(context.Context, GeneratorMixin):
return "<%s of %s>" % (type(self).__name__, self._func_execution_context) return "<%s of %s>" % (type(self).__name__, self._func_execution_context)
class Comprehension(IterableWrapper): class Comprehension(object):
@staticmethod @staticmethod
def from_atom(evaluator, atom): def from_atom(evaluator, atom):
bracket = atom.children[0] bracket = atom.children[0]
@@ -212,7 +221,7 @@ class Comprehension(IterableWrapper):
yield result yield result
except IndexError: except IndexError:
iterated = evaluator.eval_element(self._eval_node()) iterated = evaluator.eval_element(self._eval_node())
if self.type == 'dict': if self.array_type == 'dict':
yield iterated, evaluator.eval_element(self._eval_node(2)) yield iterated, evaluator.eval_element(self._eval_node(2))
else: else:
yield iterated yield iterated
@@ -239,7 +248,7 @@ class ArrayMixin(object):
@memoize_default() @memoize_default()
def names_dicts(self, search_global=False): # Always False. def names_dicts(self, search_global=False): # Always False.
# `array.type` is a string with the type, e.g. 'list'. # `array.type` is a string with the type, e.g. 'list'.
scope = compiled.builtin_from_name(self.evaluator, self.type) scope = compiled.builtin_from_name(self.evaluator, self.array_type)
# builtins only have one class -> [0] # builtins only have one class -> [0]
scopes = self.evaluator.execute_evaluated(scope, self) scopes = self.evaluator.execute_evaluated(scope, self)
names_dicts = list(scopes)[0].names_dicts(search_global) names_dicts = list(scopes)[0].names_dicts(search_global)
@@ -248,6 +257,7 @@ class ArrayMixin(object):
def get_filters(self, search_global, until_position=None, origin_scope=None): def get_filters(self, search_global, until_position=None, origin_scope=None):
# `array.type` is a string with the type, e.g. 'list'. # `array.type` is a string with the type, e.g. 'list'.
compiled_obj = compiled.builtin_from_name(self.evaluator, self.array_type) compiled_obj = compiled.builtin_from_name(self.evaluator, self.array_type)
yield SpecialMethodFilter(self, self.builtin_methods, compiled_obj)
for typ in compiled_obj.execute_evaluated(self): for typ in compiled_obj.execute_evaluated(self):
for filter in typ.get_filters(): for filter in typ.get_filters():
yield filter yield filter
@@ -258,7 +268,7 @@ class ArrayMixin(object):
return None # We don't know the length, because of appends. return None # We don't know the length, because of appends.
def py__class__(self): def py__class__(self):
return compiled.builtin_from_name(self.evaluator, self.type) return compiled.builtin_from_name(self.evaluator, self.array_type)
@safe_property @safe_property
def parent(self): def parent(self):
@@ -267,12 +277,14 @@ class ArrayMixin(object):
def dict_values(self): def dict_values(self):
return unite(self._defining_context.eval_node(v) for k, v in self._items()) return unite(self._defining_context.eval_node(v) for k, v in self._items())
@register_builtin_method('values', type='dict')
class DICT(object):
@register_builtin_method('values')
def _imitate_values(self): def _imitate_values(self):
items = self.dict_values() items = self.dict_values()
return create_evaluated_sequence_set(self.evaluator, items, sequence_type='list') return create_evaluated_sequence_set(self.evaluator, items, sequence_type='list')
@register_builtin_method('items', type='dict') @register_builtin_method('items')
def _imitate_items(self): def _imitate_items(self):
items = [set([FakeSequence(self.evaluator, (k, v), 'tuple')]) items = [set([FakeSequence(self.evaluator, (k, v), 'tuple')])
for k, v in self._items()] for k, v in self._items()]
@@ -322,7 +334,7 @@ class DictComprehension(Comprehension, ArrayMixin):
def dict_values(self): def dict_values(self):
return unite(values for keys, values in self._iterate()) return unite(values for keys, values in self._iterate())
@register_builtin_method('items', type='dict') @register_builtin_method('items')
def _imitate_items(self): def _imitate_items(self):
items = set(FakeSequence(self.evaluator, items = set(FakeSequence(self.evaluator,
(AlreadyEvaluated(keys), AlreadyEvaluated(values)), 'tuple') (AlreadyEvaluated(keys), AlreadyEvaluated(values)), 'tuple')
@@ -397,7 +409,7 @@ class ArrayLiteralContext(ArrayMixin, AbstractSequence):
def _values(self): def _values(self):
"""Returns a list of a list of node.""" """Returns a list of a list of node."""
if self.type == 'dict': if self.array_type == 'dict':
return unite(v for k, v in self._items()) return unite(v for k, v in self._items())
else: else:
return self._items() return self._items()
@@ -810,7 +822,7 @@ def check_array_instances(evaluator, instance):
return param.Arguments(evaluator, instance, [AlreadyEvaluated([ai])]) return param.Arguments(evaluator, instance, [AlreadyEvaluated([ai])])
class _ArrayInstance(IterableWrapper): class _ArrayInstance(object):
""" """
Used for the usage of set() and list(). Used for the usage of set() and list().
This is definitely a hack, but a good one :-) This is definitely a hack, but a good one :-)

View File

@@ -31,7 +31,7 @@ def literals_to_types(evaluator, result):
# Literals are only valid as long as the operations are # Literals are only valid as long as the operations are
# correct. Otherwise add a value-free instance. # correct. Otherwise add a value-free instance.
cls = builtin_from_name(evaluator, typ.name.string_name) cls = builtin_from_name(evaluator, typ.name.string_name)
new_result |= evaluator.execute(cls) new_result |= cls.execute_evaluated([])
else: else:
new_result.add(typ) new_result.add(typ)
return new_result return new_result

View File

@@ -674,9 +674,9 @@ class FunctionExecutionContext(Executed):
@recursion.execution_recursion_decorator @recursion.execution_recursion_decorator
def get_yield_values(self): def get_yield_values(self):
yields = self.funcdef.yields for_parents = [(y, tree.search_ancestor(y, ('for_stmt', 'funcdef',
stopAt = tree.ForStmt, tree.WhileStmt, tree.IfStmt 'while_stmt', 'if_stmt')))
for_parents = [(x, x.get_parent_until((stopAt))) for x in yields] for y in self.funcdef.yields]
# Calculate if the yields are placed within the same for loop. # Calculate if the yields are placed within the same for loop.
yields_order = [] yields_order = []
@@ -687,13 +687,13 @@ class FunctionExecutionContext(Executed):
parent = for_stmt.parent parent = for_stmt.parent
if parent.type == 'suite': if parent.type == 'suite':
parent = parent.parent parent = parent.parent
if for_stmt.type == 'for_stmt' and parent == self \ if for_stmt.type == 'for_stmt' and parent == self.func_def \
and for_stmt.defines_one_name(): # Simplicity for now. and for_stmt.defines_one_name(): # Simplicity for now.
if for_stmt == last_for_stmt: if for_stmt == last_for_stmt:
yields_order[-1][1].append(yield_) yields_order[-1][1].append(yield_)
else: else:
yields_order.append((for_stmt, [yield_])) yields_order.append((for_stmt, [yield_]))
elif for_stmt == self: elif for_stmt == self.funcdef:
yields_order.append((None, [yield_])) yields_order.append((None, [yield_]))
else: else:
yield self.get_return_values(check_yields=True) yield self.get_return_values(check_yields=True)