forked from VimPlug/jedi
Fixed all generator tests.
This commit is contained in:
@@ -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):
|
||||||
|
|||||||
@@ -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__():
|
||||||
|
|||||||
@@ -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 :-)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user