Start creating py__getitem__.

This commit is contained in:
Dave Halter
2015-12-04 12:08:29 +01:00
parent 76345c0b58
commit db060c70c9
7 changed files with 76 additions and 45 deletions

View File

@@ -147,7 +147,7 @@ class Evaluator(object):
types = self.eval_element(rhs)
if seek_name:
types = finder.check_tuple_assignments(types, seek_name)
types = finder.check_tuple_assignments(self, types, seek_name)
first_operation = stmt.first_operation()
if first_operation not in ('=', None) and not isinstance(stmt, er.InstanceElement): # TODO don't check for this.
@@ -352,21 +352,18 @@ class Evaluator(object):
trailer_op, node = trailer.children[:2]
if node == ')': # `arglist` is optional.
node = ()
new_types = set()
for typ in types:
debug.dbg('eval_trailer: %s in scope %s', trailer, typ)
if trailer_op == '.':
new_types |= self.find_types(typ, node)
elif trailer_op == '(':
new_types |= self.execute(typ, node, trailer)
elif trailer_op == '[':
try:
get = typ.get_index_types
except AttributeError:
debug.warning("TypeError: '%s' object is not subscriptable"
% typ)
else:
new_types |= get(self, node)
if trailer_op == '[':
for trailer_typ in self.eval_element(node):
new_types |= iterable.py__getitem__(self, types, trailer_typ, trailer_op)
else:
for typ in types:
debug.dbg('eval_trailer: %s in scope %s', trailer, typ)
if trailer_op == '.':
new_types |= self.find_types(typ, node)
elif trailer_op == '(':
new_types |= self.execute(typ, node, trailer)
return new_types
def execute_evaluated(self, obj, *args):

View File

@@ -20,6 +20,7 @@ CODES = {
'type-error-operation': (11, TypeError, None),
'type-error-not-iterable': (12, TypeError, None),
'type-error-isinstance': (13, TypeError, None),
'type-error-not-subscriptable': (13, TypeError, None),
}

View File

@@ -302,7 +302,7 @@ def _name_to_types(evaluator, name, scope):
if typ.isinstance(tree.ForStmt, tree.CompFor):
container_types = evaluator.eval_element(typ.children[3])
for_types = iterable.py__iter__types(evaluator, container_types, typ.children[3])
types = check_tuple_assignments(for_types, name)
types = check_tuple_assignments(evaluator, for_types, name)
elif isinstance(typ, tree.Param):
types = _eval_param(evaluator, typ, scope)
elif typ.isinstance(tree.ExprStmt):
@@ -542,26 +542,20 @@ def global_names_dict_generator(evaluator, scope, position):
yield names_dict, None
def check_tuple_assignments(types, name):
def check_tuple_assignments(evaluator, types, name):
"""
Checks if tuples are assigned.
"""
for index in name.assignment_indexes():
new_types = set()
for r in types:
for index, node in name.assignment_indexes():
iterated = iterable.py__iter__(evaluator, types, node)
all_types = set()
for _ in range(index + 1):
try:
func = r.get_exact_index_types
except AttributeError:
debug.warning("Invalid tuple lookup #%s of result %s in %s",
index, types, name)
else:
if isinstance(r, iterable.Array) and r.type == 'dict':
continue
try:
new_types |= func(index)
except IndexError:
pass
types = new_types
types = next(iterated)
all_types |= types
except StopIteration:
types = all_types
break
return types

View File

@@ -23,13 +23,12 @@ It is important to note that:
from jedi.common import unite, ignored, safe_property
from jedi import debug
from jedi import settings
from jedi._compatibility import use_metaclass, is_py3, unicode
from jedi._compatibility import use_metaclass, unicode
from jedi.parser import tree
from jedi.evaluate import compiled
from jedi.evaluate import helpers
from jedi.evaluate.cache import CachedMetaClass, memoize_default
from jedi.evaluate import analysis
from jedi.evaluate.precedence import literals_to_types
class IterableWrapper(tree.Base):
@@ -269,8 +268,8 @@ class Array(IterableWrapper, ArrayMixin):
result |= check_array_additions(self._evaluator, self)
return result
def get_exact_index_types(self, mixed_index):
""" Here the index is an int/str. Raises IndexError/KeyError """
def py__getitem__(self, index):
"""Here the index is an int/str. Raises IndexError/KeyError."""
if self.type == 'dict':
for key, values in self._items():
# Because we only want the key to be a string.
@@ -278,13 +277,13 @@ class Array(IterableWrapper, ArrayMixin):
for k in keys:
if isinstance(k, compiled.CompiledObject) \
and mixed_index == k.obj:
and index == k.obj:
for value in values:
return self._evaluator.eval_element(value)
raise KeyError('No key found in dictionary %s.' % self)
# Can raise an IndexError
return self._evaluator.eval_element(self._items()[mixed_index])
return self._evaluator.eval_element(self._items()[index])
def iter_content(self):
return self.values()
@@ -466,19 +465,20 @@ def unpack_tuple_to_dict(evaluator, types, exprlist):
raise NotImplementedError
def py__iter__(evaluator, types, node):
def py__iter__(evaluator, types, node=None):
debug.dbg('py__iter__')
for typ in types:
try:
iter_method = typ.py__iter__
except AttributeError:
analysis.add(evaluator, 'type-error-not-iterable', node)
if node is not None:
analysis.add(evaluator, 'type-error-not-iterable', node)
else:
for result in iter_method():
yield result
def py__iter__types(evaluator, types, node):
def py__iter__types(evaluator, types, node=None):
"""
Calls `py__iter__`, but ignores the ordering in the end and just returns
all types that it contains.
@@ -486,6 +486,33 @@ def py__iter__types(evaluator, types, node):
return unite(py__iter__(evaluator, types, node))
def py__getitem__(evaluator, types, index, node):
result = set()
# Index handling.
if isinstance(index, compiled.CompiledObject):
pure_index = index.obj
elif not type(index) in (float, int, str, unicode):
pure_index = index
else:
# If the index is not clearly defined, we have to get all the
# possiblities.
return py__iter__types(evaluator, types)
for typ in types:
# The actual getitem call.
try:
getitem = typ.py__getitem__
except AttributeError:
analysis.add(evaluator, 'type-error-not-subscriptable', node)
else:
try:
result |= getitem(pure_index)
except IndexError:
return py__iter__types(evaluator, set([typ]))
return result
def check_array_additions(evaluator, array):
""" Just a mapper function for the internal _check_array_additions """
if array.type not in ('list', 'set'):

View File

@@ -22,6 +22,9 @@ py__bool__() Returns True/False/None; None means that
there's no certainty.
py__bases__(evaluator) Returns a list of base classes.
py__mro__(evaluator) Returns a list of classes (the mro).
py__iter__() Returns a generator of a set of types.
py__getitem__(index: int/str) Returns a a set of types of the index.
Can raise an IndexError/KeyError.
py__getattribute__(evaluator, name) Returns a list of attribute values. The
name can be str or Name.
====================================== ========================================

View File

@@ -319,14 +319,14 @@ class Name(Leaf):
def assignment_indexes(self):
"""
Returns an array of ints of the indexes that are used in tuple
assignments.
Returns an array of tuple(int, node) of the indexes that are used in
tuple assignments.
For example if the name is ``y`` in the following code::
x, (y, z) = 2, ''
would result in ``[1, 0]``.
would result in ``[(1, xyz_node), (0, yz_node)]``.
"""
indexes = []
node = self.parent
@@ -335,7 +335,7 @@ class Name(Leaf):
if is_node(node, 'testlist_comp', 'testlist_star_expr', 'exprlist'):
for i, child in enumerate(node.children):
if child == compare:
indexes.insert(0, int(i / 2))
indexes.insert(0, (int(i / 2), node))
break
else:
raise LookupError("Couldn't find the assignment.")

View File

@@ -209,6 +209,15 @@ dic2[r'as' 'd' u'f']
#? int() str()
dic2['just_something']
# unpacking
a, b = dic2
#? str()
a
a, b = {1: 'x', 2.0: 1j}
#? int() float()
a
def f():
""" github #83 """
r = {}