1
0
forked from VimPlug/jedi

created evaluate.iterable to push arrays and generators into a seperate file

This commit is contained in:
Dave Halter
2013-12-30 01:02:18 +01:00
parent 8561217333
commit e4692381cb
8 changed files with 201 additions and 192 deletions

View File

@@ -12,6 +12,7 @@ from jedi import common
from jedi.parser import representation as pr
from jedi import cache
from jedi.evaluate import representation as er
from jedi.evaluate import iterable
from jedi.evaluate import imports
from jedi import keywords
@@ -440,7 +441,7 @@ class Definition(BaseDefinition):
if isinstance(d, pr.Name):
return d.names[-1] if d.names else None
elif isinstance(d, er.Array):
elif isinstance(d, iterable.Array):
return unicode(d.type)
elif isinstance(d, (pr.Class, er.Class, er.Instance,
er.Function, pr.Function)):
@@ -493,7 +494,7 @@ class Definition(BaseDefinition):
if isinstance(d, pr.Name):
d = d.parent
if isinstance(d, er.Array):
if isinstance(d, iterable.Array):
d = 'class ' + d.type
elif isinstance(d, (pr.Class, er.Class, er.Instance)):
d = 'class ' + unicode(d.name)

View File

@@ -81,6 +81,7 @@ from jedi.evaluate import representation as er
from jedi.evaluate import builtin
from jedi.evaluate import imports
from jedi.evaluate import recursion
from jedi.evaluate import iterable
from jedi.evaluate.cache import memoize_default
from jedi import docstrings
from jedi.evaluate import dynamic
@@ -105,7 +106,7 @@ def get_defined_names_for_position(scope, position=None, start_scope=None):
names = scope.get_defined_names()
# Instances have special rules, always return all the possible completions,
# because class variables are always valid and the `self.` variables, too.
if (not position or isinstance(scope, (er.Array, er.Instance))
if (not position or isinstance(scope, (iterable.Array, er.Instance))
or start_scope != scope
and isinstance(start_scope, (pr.Function, er.FunctionExecution))):
return names
@@ -563,7 +564,7 @@ class Evaluator(object):
continue
result += self.eval_call(call)
elif call == '*':
if [r for r in result if isinstance(r, er.Array)
if [r for r in result if isinstance(r, iterable.Array)
or isinstance(r, er.Instance)
and str(r.name) == 'str']:
# if it is an iterable, ignore * operations
@@ -587,7 +588,7 @@ class Evaluator(object):
current = next(path)
if isinstance(current, pr.Array):
types = [er.Array(self, current)]
types = [iterable.Array(self, current)]
else:
if isinstance(current, pr.NamePart):
# This is the first global lookup.
@@ -677,7 +678,7 @@ class Evaluator(object):
if obj.isinstance(er.Class):
# There maybe executions of executions.
return [er.Instance(self, obj, params)]
elif isinstance(obj, er.Generator):
elif isinstance(obj, iterable.Generator):
return obj.iter_content()
else:
stmts = []
@@ -763,7 +764,7 @@ def get_iterator_types(inputs):
# Take the first statement (for has always only
# one, remember `in`). And follow it.
for it in inputs:
if isinstance(it, (er.Generator, er.Array, dynamic.ArrayInstance)):
if isinstance(it, (iterable.Generator, iterable.Array, dynamic.ArrayInstance)):
iterators.append(it)
else:
if not hasattr(it, 'execute_subscope_by_name'):
@@ -776,7 +777,7 @@ def get_iterator_types(inputs):
result = []
for gen in iterators:
if isinstance(gen, er.Array):
if isinstance(gen, iterable.Array):
# Array is a little bit special, since this is an internal
# array, but there's also the list builtin, which is
# another thing.

View File

@@ -333,10 +333,11 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
from jedi.evaluate import representation as er
from jedi import evaluate
from jedi.evaluate import iterable
def get_execution_parent(element, *stop_classes):
""" Used to get an Instance/FunctionExecution parent """
if isinstance(element, er.Array):
if isinstance(element, iterable.Array):
stmt = element._array.parent
else:
# is an Instance with an ArrayInstance inside
@@ -463,7 +464,7 @@ def check_flow_information(evaluator, flow, search_name, pos):
def _check_isinstance_type(evaluator, stmt, search_name):
from jedi.evaluate import representation as er
from jedi.evaluate import iterable
try:
expression_list = stmt.expression_list()
# this might be removed if we analyze and, etc
@@ -487,6 +488,6 @@ def _check_isinstance_type(evaluator, stmt, search_name):
result = []
for c in evaluator.eval_call(classes[0]):
for typ in (c.get_index_types() if isinstance(c, er.Array) else [c]):
for typ in (c.get_index_types() if isinstance(c, iterable.Array) else [c]):
result += evaluator.execute(typ)
return result

View File

@@ -1,3 +0,0 @@
class Iterable():
"""Parent class of Generator and Array, exists due to import restrictions."""
pass

182
jedi/evaluate/iterable.py Normal file
View File

@@ -0,0 +1,182 @@
import itertools
from jedi._compatibility import use_metaclass
from jedi import common
from jedi.parser import representation as pr
from jedi import debug
from jedi.evaluate import builtin
from jedi.evaluate import dynamic
from jedi.evaluate.cache import CachedMetaClass
class Generator(use_metaclass(CachedMetaClass, pr.Base)):
""" Cares for `yield` statements. """
def __init__(self, evaluator, func, var_args):
super(Generator, self).__init__()
self._evaluator = evaluator
self.func = func
self.var_args = var_args
def get_defined_names(self):
"""
Returns a list of names that define a generator, which can return the
content of a generator.
"""
names = []
none_pos = (0, 0)
executes_generator = ('__next__', 'send')
for n in ('close', 'throw') + executes_generator:
name = pr.Name(builtin.Builtin.scope, [(n, none_pos)],
none_pos, none_pos)
if n in executes_generator:
name.parent = self
else:
name.parent = builtin.Builtin.scope
names.append(name)
debug.dbg('generator names', names)
return names
def iter_content(self):
""" returns the content of __iter__ """
return self._evaluator.execute(self.func, self.var_args, True)
def get_index_types(self, index=None):
debug.warning('Tried to get array access on a generator', self)
return []
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'get_imports',
'asserts', 'doc', 'docstr', 'get_parent_until',
'get_code', 'subscopes']:
raise AttributeError("Accessing %s of %s is not allowed."
% (self, name))
return getattr(self.func, name)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.func)
class Array(use_metaclass(CachedMetaClass, pr.Base)):
"""
Used as a mirror to pr.Array, if needed. It defines some getter
methods which are important in this module.
"""
def __init__(self, evaluator, array):
self._evaluator = evaluator
self._array = array
def get_index_types(self, index_arr=None):
""" Get the types of a specific index or all, if not given """
if index_arr is not None:
if index_arr and [x for x in index_arr if ':' in x.expression_list()]:
# array slicing
return [self]
index_possibilities = self._follow_values(index_arr)
if len(index_possibilities) == 1:
# This is indexing only one element, with a fixed index number,
# otherwise it just ignores the index (e.g. [1+1]).
index = index_possibilities[0]
from jedi.evaluate.representation import Instance
if isinstance(index, Instance) \
and str(index.name) in ['int', 'str'] \
and len(index.var_args) == 1:
# TODO this is just very hackish and a lot of use cases are
# being ignored
with common.ignored(KeyError, IndexError,
UnboundLocalError, TypeError):
return self.get_exact_index_types(index.var_args[0])
result = list(self._follow_values(self._array.values))
result += dynamic.check_array_additions(self._evaluator, self)
return set(result)
def get_exact_index_types(self, mixed_index):
""" Here the index is an int/str. Raises IndexError/KeyError """
index = mixed_index
if self.type == pr.Array.DICT:
index = None
for i, key_statement in enumerate(self._array.keys):
# Because we only want the key to be a string.
key_expression_list = key_statement.expression_list()
if len(key_expression_list) != 1: # cannot deal with complex strings
continue
key = key_expression_list[0]
if isinstance(key, pr.String):
str_key = key.value
elif isinstance(key, pr.Name):
str_key = str(key)
if mixed_index == str_key:
index = i
break
if index is None:
raise KeyError('No key found in dictionary')
# Can raise an IndexError
values = [self._array.values[index]]
return self._follow_values(values)
def _follow_values(self, values):
""" helper function for the index getters """
return list(itertools.chain.from_iterable(self._evaluator.eval_statement(v)
for v in values))
def get_defined_names(self):
"""
This method generates all `ArrayMethod` for one pr.Array.
It returns e.g. for a list: append, pop, ...
"""
# `array.type` is a string with the type, e.g. 'list'.
scope = self._evaluator.find_name(builtin.Builtin.scope, self._array.type)[0]
scope = self._evaluator.execute(scope)[0] # builtins only have one class
names = scope.get_defined_names()
return [ArrayMethod(n) for n in names]
@property
def parent(self):
return builtin.Builtin.scope
def get_parent_until(self):
return builtin.Builtin.scope
def __getattr__(self, name):
if name not in ['type', 'start_pos', 'get_only_subelement', 'parent',
'get_parent_until', 'items']:
raise AttributeError('Strange access on %s: %s.' % (self, name))
return getattr(self._array, name)
def __getitem__(self):
return self._array.__getitem__()
def __iter__(self):
return self._array.__iter__()
def __len__(self):
return self._array.__len__()
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self._array)
class ArrayMethod(object):
"""
A name, e.g. `list.append`, it is used to access the original array
methods.
"""
def __init__(self, name):
super(ArrayMethod, self).__init__()
self.name = name
def __getattr__(self, name):
# Set access privileges:
if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']:
raise AttributeError('Strange accesson %s: %s.' % (self, name))
return getattr(self.name, name)
def get_parent_until(self):
return builtin.Builtin.scope
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.name)

View File

@@ -11,7 +11,7 @@ from jedi.parser import representation as pr
from jedi import debug
from jedi import settings
from jedi.evaluate import builtin
from jedi.evaluate import interfaces
from jedi.evaluate import iterable
def recursion_decorator(func):
@@ -145,7 +145,7 @@ class ExecutionRecursionDetector(object):
if cls.execution_count > settings.max_executions:
return True
if isinstance(execution.base, interfaces.Iterable):
if isinstance(execution.base, (iterable.Array, iterable.Generator)):
return False
module = execution.get_parent_until()
if evaluate_generator or module == builtin.Builtin.scope:

View File

@@ -9,10 +9,7 @@ instantiated. This class represents these cases.
So, why is there also a ``Class`` class here? Well, there are decorators and
they change classes in Python 3.
"""
from __future__ import with_statement
import copy
import itertools
from jedi._compatibility import use_metaclass, next, unicode
from jedi.parser import representation as pr
@@ -22,7 +19,7 @@ from jedi import common
from jedi.evaluate import builtin
from jedi.evaluate import recursion
from jedi.evaluate.cache import memoize_default, CachedMetaClass
from jedi.evaluate.interfaces import Iterable
from jedi.evaluate.iterable import Array, Generator
from jedi import docstrings
from jedi.evaluate import dynamic
@@ -680,174 +677,3 @@ class FunctionExecution(Executable):
def __repr__(self):
return "<%s of %s>" % \
(type(self).__name__, self.base)
class Generator(use_metaclass(CachedMetaClass, pr.Base, Iterable)):
""" Cares for `yield` statements. """
def __init__(self, evaluator, func, var_args):
super(Generator, self).__init__()
self._evaluator = evaluator
self.func = func
self.var_args = var_args
def get_defined_names(self):
"""
Returns a list of names that define a generator, which can return the
content of a generator.
"""
names = []
none_pos = (0, 0)
executes_generator = ('__next__', 'send')
for n in ('close', 'throw') + executes_generator:
name = pr.Name(builtin.Builtin.scope, [(n, none_pos)],
none_pos, none_pos)
if n in executes_generator:
name.parent = self
else:
name.parent = builtin.Builtin.scope
names.append(name)
debug.dbg('generator names', names)
return names
def iter_content(self):
""" returns the content of __iter__ """
return self._evaluator.execute(self.func, self.var_args, True)
def get_index_types(self, index=None):
debug.warning('Tried to get array access on a generator', self)
return []
def __getattr__(self, name):
if name not in ['start_pos', 'end_pos', 'parent', 'get_imports',
'asserts', 'doc', 'docstr', 'get_parent_until',
'get_code', 'subscopes']:
raise AttributeError("Accessing %s of %s is not allowed."
% (self, name))
return getattr(self.func, name)
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.func)
class Array(use_metaclass(CachedMetaClass, pr.Base, Iterable)):
"""
Used as a mirror to pr.Array, if needed. It defines some getter
methods which are important in this module.
"""
def __init__(self, evaluator, array):
self._evaluator = evaluator
self._array = array
def get_index_types(self, index_arr=None):
""" Get the types of a specific index or all, if not given """
if index_arr is not None:
if index_arr and [x for x in index_arr if ':' in x.expression_list()]:
# array slicing
return [self]
index_possibilities = self._follow_values(index_arr)
if len(index_possibilities) == 1:
# This is indexing only one element, with a fixed index number,
# otherwise it just ignores the index (e.g. [1+1]).
index = index_possibilities[0]
if isinstance(index, Instance) \
and str(index.name) in ['int', 'str'] \
and len(index.var_args) == 1:
# TODO this is just very hackish and a lot of use cases are
# being ignored
with common.ignored(KeyError, IndexError,
UnboundLocalError, TypeError):
return self.get_exact_index_types(index.var_args[0])
result = list(self._follow_values(self._array.values))
result += dynamic.check_array_additions(self._evaluator, self)
return set(result)
def get_exact_index_types(self, mixed_index):
""" Here the index is an int/str. Raises IndexError/KeyError """
index = mixed_index
if self.type == pr.Array.DICT:
index = None
for i, key_statement in enumerate(self._array.keys):
# Because we only want the key to be a string.
key_expression_list = key_statement.expression_list()
if len(key_expression_list) != 1: # cannot deal with complex strings
continue
key = key_expression_list[0]
if isinstance(key, pr.String):
str_key = key.value
elif isinstance(key, pr.Name):
str_key = str(key)
if mixed_index == str_key:
index = i
break
if index is None:
raise KeyError('No key found in dictionary')
# Can raise an IndexError
values = [self._array.values[index]]
return self._follow_values(values)
def _follow_values(self, values):
""" helper function for the index getters """
return list(itertools.chain.from_iterable(self._evaluator.eval_statement(v)
for v in values))
def get_defined_names(self):
"""
This method generates all `ArrayMethod` for one pr.Array.
It returns e.g. for a list: append, pop, ...
"""
# `array.type` is a string with the type, e.g. 'list'.
scope = self._evaluator.find_name(builtin.Builtin.scope, self._array.type)[0]
scope = self._evaluator.execute(scope)[0] # builtins only have one class
names = scope.get_defined_names()
return [ArrayMethod(n) for n in names]
@property
def parent(self):
return builtin.Builtin.scope
def get_parent_until(self):
return builtin.Builtin.scope
def __getattr__(self, name):
if name not in ['type', 'start_pos', 'get_only_subelement', 'parent',
'get_parent_until', 'items']:
raise AttributeError('Strange access on %s: %s.' % (self, name))
return getattr(self._array, name)
def __getitem__(self):
return self._array.__getitem__()
def __iter__(self):
return self._array.__iter__()
def __len__(self):
return self._array.__len__()
def __repr__(self):
return "<e%s of %s>" % (type(self).__name__, self._array)
class ArrayMethod(object):
"""
A name, e.g. `list.append`, it is used to access the original array
methods.
"""
def __init__(self, name):
super(ArrayMethod, self).__init__()
self.name = name
def __getattr__(self, name):
# Set access privileges:
if name not in ['parent', 'names', 'start_pos', 'end_pos', 'get_code']:
raise AttributeError('Strange accesson %s: %s.' % (self, name))
return getattr(self.name, name)
def get_parent_until(self):
return builtin.Builtin.scope
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.name)

View File

@@ -5,6 +5,7 @@ understand them with Jedi.
from jedi.evaluate import builtin
from jedi.evaluate import representation as er
from jedi.evaluate import iterable
from jedi.parser import representation as pr
from jedi import debug
@@ -14,7 +15,7 @@ class NotInStdLib(LookupError):
def execute(evaluator, obj, params):
if not isinstance(obj, (er.Generator, er.Array)):
if not isinstance(obj, (iterable.Generator, iterable.Array)):
obj_name = str(obj.name)
if obj.parent == builtin.Builtin.scope:
# for now we just support builtin functions.