1
0
forked from VimPlug/jedi

dev/refactor merge

This commit is contained in:
David Halter
2013-02-23 20:53:57 +04:30
23 changed files with 1042 additions and 1008 deletions

View File

@@ -72,7 +72,6 @@ from _compatibility import next, hasattr, is_py3k, unicode, utf8
import sys
import itertools
import copy
import common
import cache
@@ -82,7 +81,6 @@ import debug
import builtin
import imports
import recursion
import helpers
import dynamic
import docstrings
@@ -249,12 +247,13 @@ def find_name(scope, name_str, position=None, search_global=False,
def handle_for_loops(loop):
# Take the first statement (for has always only
# one, remember `in`). And follow it.
if not len(loop.inits):
if not loop.inputs:
return []
result = get_iterator_types(follow_statement(loop.inits[0]))
result = get_iterator_types(follow_statement(loop.inputs[0]))
if len(loop.set_vars) > 1:
var_arr = loop.set_stmt.get_assignment_calls()
result = assign_tuples(var_arr, result, name_str)
commands = loop.set_stmt.get_commands()
# loops with loop.set_vars > 0 only have one command
result = assign_tuples(commands[0], result, name_str)
return result
def process(name):
@@ -286,22 +285,21 @@ def find_name(scope, name_str, position=None, search_global=False,
inst.is_generated = True
result.append(inst)
elif par.isinstance(pr.Statement):
def is_execution(arr):
for a in arr:
a = a[0] # rest is always empty with assignees
if a.isinstance(pr.Array):
if is_execution(a):
def is_execution(calls):
for c in calls:
if c.isinstance(pr.Array):
if is_execution(c):
return True
elif a.isinstance(pr.Call):
elif c.isinstance(pr.Call):
# Compare start_pos, because names may be different
# because of executions.
if a.name.start_pos == name.start_pos \
and a.execution:
if c.name.start_pos == name.start_pos \
and c.execution:
return True
return False
is_exe = False
for op, assignee in par.assignment_details:
for assignee, op in par.assignment_details:
is_exe |= is_execution(assignee)
if is_exe:
@@ -310,7 +308,7 @@ def find_name(scope, name_str, position=None, search_global=False,
pass
else:
details = par.assignment_details
if details and details[0][0] != '=':
if details and details[0][1] != '=':
no_break_scope = True
# TODO this makes self variables non-breakable. wanted?
@@ -374,7 +372,8 @@ def find_name(scope, name_str, position=None, search_global=False,
if not result and isinstance(nscope, er.Instance):
# __getattr__ / __getattribute__
result += check_getattr(nscope, name_str)
debug.dbg('sfn filter "%s" in %s: %s' % (name_str, nscope, result))
debug.dbg('sfn filter "%s" in (%s-%s): %s@%s' % (name_str, scope,
nscope, result, position))
return result
def descriptor_check(result):
@@ -415,10 +414,10 @@ def check_getattr(inst, name_str):
"""Checks for both __getattr__ and __getattribute__ methods"""
result = []
# str is important to lose the NamePart!
name = pr.Call(str(name_str), pr.Call.STRING, (0, 0), inst)
args = helpers.generate_param_array([name])
module = builtin.Builtin.scope
name = pr.Call(module, str(name_str), pr.Call.STRING, (0, 0), inst)
try:
result = inst.execute_subscope_by_name('__getattr__', args)
result = inst.execute_subscope_by_name('__getattr__', [name])
except KeyError:
pass
if not result:
@@ -427,7 +426,7 @@ def check_getattr(inst, name_str):
# could be practical and the jedi would return wrong types. If
# you ever have something, let me know!
try:
result = inst.execute_subscope_by_name('__getattribute__', args)
result = inst.execute_subscope_by_name('__getattribute__', [name])
except KeyError:
pass
return result
@@ -485,46 +484,43 @@ def assign_tuples(tup, results, seek_name):
def eval_results(index):
types = []
for r in results:
if hasattr(r, "get_exact_index_types"):
try:
types += r.get_exact_index_types(index)
except IndexError:
pass
else:
try:
func = r.get_exact_index_types
except AttributeError:
debug.warning("invalid tuple lookup %s of result %s in %s"
% (tup, results, seek_name))
else:
try:
types += func(index)
except IndexError:
pass
return types
result = []
if tup.type == pr.Array.NOARRAY:
# Here we have unnessecary braces, which we just remove.
arr = tup.get_only_subelement()
if type(arr) == pr.Call:
if arr.name.names[-1] == seek_name:
result = results
else:
result = assign_tuples(arr, results, seek_name)
else:
for i, t in enumerate(tup):
# Used in assignments. There is just one call and no other things,
# therefore we can just assume, that the first part is important.
if len(t) != 1:
raise AttributeError('Array length should be 1')
t = t[0]
for i, stmt in enumerate(tup):
# Used in assignments. There is just one call and no other things,
# therefore we can just assume, that the first part is important.
command = stmt.get_commands()[0]
# Check the left part, if there are still tuples in it or a Call.
if isinstance(t, pr.Array):
# These are "sub"-tuples.
result += assign_tuples(t, eval_results(i), seek_name)
else:
if t.name.names[-1] == seek_name:
result += eval_results(i)
if tup.type == pr.Array.NOARRAY:
# unnessecary braces -> just remove.
r = results
else:
r = eval_results(i)
# are there still tuples or is it just a Call.
if isinstance(command, pr.Array):
# These are "sub"-tuples.
result += assign_tuples(command, r, seek_name)
else:
if command.name.names[-1] == seek_name:
result += r
return result
@recursion.RecursionDecorator
@cache.memoize_default(default=[])
@cache.memoize_default(default=())
def follow_statement(stmt, seek_name=None):
"""
The starting point of the completion. A statement always owns a call list,
@@ -536,11 +532,11 @@ def follow_statement(stmt, seek_name=None):
:param seek_name: A string.
"""
debug.dbg('follow_stmt %s (%s)' % (stmt, seek_name))
call_list = stmt.get_assignment_calls()
debug.dbg('calls: %s' % call_list)
commands = stmt.get_commands()
debug.dbg('calls: %s' % commands)
try:
result = follow_call_list(call_list)
result = follow_call_list(commands)
except AttributeError:
# This is so evil! But necessary to propagate errors. The attribute
# errors here must not be catched, because they shouldn't exist.
@@ -550,16 +546,15 @@ def follow_statement(stmt, seek_name=None):
# variables.
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
new_result = []
for op, set_vars in stmt.assignment_details:
new_result += assign_tuples(set_vars, result, seek_name)
for ass_commands, op in stmt.assignment_details:
new_result += assign_tuples(ass_commands[0], result, seek_name)
result = new_result
return set(result)
def follow_call_list(call_list, follow_array=False):
"""
The call_list has a special structure.
This can be either `pr.Array` or `list of list`.
`call_list` can be either `pr.Array` or `list of list`.
It is used to evaluate a two dimensional object, that has calls, arrays and
operators in it.
"""
@@ -570,79 +565,72 @@ def follow_call_list(call_list, follow_array=False):
# is nested LC
input = nested_lc.stmt
module = input.get_parent_until()
loop = pr.ForFlow(module, [input], lc.stmt.start_pos,
lc.middle, True)
# create a for loop, which does the same as list comprehensions
loop = pr.ForFlow(module, [input], lc.stmt.start_pos, lc.middle, True)
loop.parent = lc.stmt.parent if parent is None else parent
loop.parent = parent or lc.get_parent_until(pr.IsScope)
if isinstance(nested_lc, pr.ListComprehension):
loop = evaluate_list_comprehension(nested_lc, loop)
return loop
if pr.Array.is_type(call_list, pr.Array.TUPLE, pr.Array.DICT):
# Tuples can stand just alone without any braces. These would be
# recognized as separate calls, but actually are a tuple.
result = follow_call(call_list)
else:
result = []
for calls in call_list:
calls_iterator = iter(calls)
for call in calls_iterator:
if pr.Array.is_type(call, pr.Array.NOARRAY):
result += follow_call_list(call, follow_array=True)
elif isinstance(call, pr.ListComprehension):
loop = evaluate_list_comprehension(call)
stmt = copy.copy(call.stmt)
stmt.parent = loop
# create a for loop which does the same as list
# comprehensions
result += follow_statement(stmt)
else:
if isinstance(call, (pr.Lambda)):
result.append(er.Function(call))
# With things like params, these can also be functions...
elif isinstance(call, (er.Function, er.Class, er.Instance,
dynamic.ArrayInstance)):
result.append(call)
# The string tokens are just operations (+, -, etc.)
elif not isinstance(call, (str, unicode)):
if str(call.name) == 'if':
# Ternary operators.
while True:
try:
call = next(calls_iterator)
except StopIteration:
break
try:
if str(call.name) == 'else':
break
except AttributeError:
pass
continue
result += follow_call(call)
elif call == '*':
if [r for r in result if isinstance(r, er.Array)
or isinstance(r, er.Instance)
and str(r.name) == 'str']:
# if it is an iterable, ignore * operations
next(calls_iterator)
if follow_array and isinstance(call_list, pr.Array):
# call_list can also be a two dimensional array
call_path = call_list.generate_call_path()
next(call_path, None) # the first one has been used already
call_scope = call_list.parent_stmt
position = call_list.start_pos
result = follow_paths(call_path, result, call_scope, position=position)
result = []
calls_iterator = iter(call_list)
for call in calls_iterator:
if pr.Array.is_type(call, pr.Array.NOARRAY):
r = list(itertools.chain.from_iterable(follow_statement(s)
for s in call))
call_path = call.generate_call_path()
next(call_path, None) # the first one has been used already
result += follow_paths(call_path, r, call.parent,
position=call.start_pos)
elif isinstance(call, pr.ListComprehension):
loop = evaluate_list_comprehension(call)
# Caveat: parents are being changed, but this doesn't matter,
# because nothing else uses it.
call.stmt.parent = loop
result += follow_statement(call.stmt)
else:
if isinstance(call, pr.Lambda):
result.append(er.Function(call))
# With things like params, these can also be functions...
elif isinstance(call, (er.Function, er.Class, er.Instance,
dynamic.ArrayInstance)):
result.append(call)
# The string tokens are just operations (+, -, etc.)
elif not isinstance(call, (str, unicode)):
if str(call.name) == 'if':
# Ternary operators.
while True:
try:
call = next(calls_iterator)
except StopIteration:
break
try:
if str(call.name) == 'else':
break
except AttributeError:
pass
continue
result += follow_call(call)
elif call == '*':
if [r for r in result if isinstance(r, er.Array)
or isinstance(r, er.Instance)
and str(r.name) == 'str']:
# if it is an iterable, ignore * operations
next(calls_iterator)
return set(result)
def follow_call(call):
"""Follow a call is following a function, variable, string, etc."""
scope = call.parent_stmt.parent
path = call.generate_call_path()
position = call.parent_stmt.start_pos
return follow_call_path(path, scope, position)
# find the statement of the Scope
s = call
while not s.parent.isinstance(pr.IsScope):
s = s.parent
return follow_call_path(path, s.parent, s.start_pos)
def follow_call_path(path, scope, position):
@@ -664,8 +652,7 @@ def follow_call_path(path, scope, position):
debug.warning('unknown type:', current.type, current)
scopes = []
# Make instances of those number/string objects.
arr = helpers.generate_param_array([current.name])
scopes = [er.Instance(s, arr) for s in scopes]
scopes = [er.Instance(s, (current.name,)) for s in scopes]
result = imports.strip_imports(scopes)
return follow_paths(path, result, scope, position=position)
@@ -745,11 +732,12 @@ def filter_private_variable(scope, call_scope, var_name):
def goto(stmt, call_path=None):
if call_path is None:
arr = stmt.get_assignment_calls()
call = arr.get_only_subelement()
commands = stmt.get_commands()
assert len(commands) == 1
call = commands[0]
call_path = list(call.generate_call_path())
scope = stmt.parent
scope = stmt.get_parent_until(pr.IsScope)
pos = stmt.start_pos
call_path, search = call_path[:-1], call_path[-1]
pos = pos[0], pos[1] + 1