1
0
forked from VimPlug/jedi

comments revised.

This commit is contained in:
David Halter
2012-07-12 01:04:11 +02:00
parent 28f12865d2
commit 19c7f470ca

View File

@@ -114,10 +114,8 @@ class CachedMetaClass(type):
class Executable(object):
""" An instance is also an executable - because __init__ is called """
def __init__(self, base, var_args=parsing.Array(None, None)):
#if var_args == []:
# raise NotImplementedError()
self.base = base
# the param input array
# The param input array.
self.var_args = var_args
def get_parent_until(self, *args):
@@ -134,7 +132,6 @@ class Instance(Executable):
@memoize_default()
def get_init_execution(self, func):
if isinstance(func, parsing.Function):
#self.set_param_cb(InstanceElement(self, Function(sub)))
instance_el = InstanceElement(self, Function(func))
return Execution(instance_el, self.var_args)
else:
@@ -163,13 +160,13 @@ class Instance(Executable):
names.append(InstanceElement(self, n))
names = []
# this loop adds the names of the self object, copies them and removes
# This loop adds the names of the self object, copies them and removes
# the self.
for sub in self.base.subscopes:
# get the self name, if there's one
# Get the self name, if there's one.
self_name = self.get_func_self_name(sub)
if self_name:
# check the __init__ function
# Check the __init__ function.
if self.var_args and sub.name.get_code() == '__init__':
sub = self.get_init_execution(sub)
for n in sub.get_set_vars():
@@ -193,7 +190,7 @@ class Instance(Executable):
class_names = self.base.get_defined_names()
for var in class_names:
# functions are also instance elements
# Functions are also instance elements.
if isinstance(var.parent, (Function, parsing.Function)):
var = InstanceElement(self, var)
names.append(var)
@@ -202,7 +199,7 @@ class Instance(Executable):
def get_descriptor_return(self, obj):
""" Throws an error if there's no method. """
method = self.get_subscope_by_name('__get__')
# arguments in __set__ descriptors are obj, class.
# Arguments in __set__ descriptors are obj, class.
# `method` is the new parent of the array, don't know if that's good.
args = parsing.Array('tuple', method, values=[[obj], [obj.base]])
method = InstanceElement(self, method)
@@ -212,7 +209,7 @@ class Instance(Executable):
def __getattr__(self, name):
if name == 'get_index_types':
# todo call __getitem__ in such cases?
# TODO Call __getitem__ in such cases?
return lambda: []
if name not in ['line_nr', 'indent', 'name', 'get_imports']:
raise AttributeError("Instance %s: Don't touch this (%s)!"
@@ -245,13 +242,12 @@ class InstanceElement(object):
return InstanceElement(self.instance, scope)
def get_assignment_calls(self):
# copy and modify the array
# Copy and modify the array.
origin = self.var.get_assignment_calls()
origin.parent_stmt, temp = None, origin.parent_stmt
new = copy.deepcopy(origin)
origin.parent_stmt = temp
new.parent_stmt = InstanceElement(self.instance, temp)
#print 'gac', new, new.parent_stmt, new.parent_stmt.instance
return new
def __getattr__(self, name):
@@ -270,13 +266,13 @@ class Class(object):
@memoize_default(default=[])
def get_super_classes(self):
supers = []
# TODO care for mro stuff (multiple super classes)
# TODO care for mro stuff (multiple super classes).
for s in self.base.supers:
# super classes are statements
# Super classes are statements.
for cls in follow_statement(s):
if not isinstance(cls, Class):
debug.warning('Received non class, as a super class')
continue # just ignore other stuff (user input error)
continue # Just ignore other stuff (user input error).
supers.append(cls)
return supers
@@ -285,7 +281,7 @@ class Class(object):
def in_iterable(name, iterable):
""" checks if the name is in the variable 'iterable'. """
for i in iterable:
# only the last name is important, because these names have a
# Only the last name is important, because these names have a
# maximal length of 2, with the first one being `self`.
if i.names[-1] == name.names[-1]:
return True
@@ -294,7 +290,7 @@ class Class(object):
result = self.base.get_defined_names()
super_result = []
for cls in self.get_super_classes():
# get the inherited names
# Get the inherited names.
for i in cls.get_defined_names():
if not in_iterable(i, result):
super_result.append(i)
@@ -334,7 +330,7 @@ class Function(object):
"""
f = self.base_func
# only enter it, if has not already been processed
# Only enter it, if has not already been processed.
if not self.is_decorated:
for dec in reversed(self.base_func.decorators):
debug.dbg('decorator:', dec, f)
@@ -347,7 +343,7 @@ class Function(object):
debug.warning('multiple decorators found', self.base_func,
dec_results)
decorator = dec_results.pop()
# create param array
# Create param array.
old_func = Function(f, is_decorated=True)
params = parsing.Array(parsing.Array.NOARRAY, old_func)
params.values = [[old_func]]
@@ -359,7 +355,7 @@ class Function(object):
if len(wrappers) > 1:
debug.warning('multiple wrappers found', self.base_func,
wrappers)
# this is here, that the wrapper gets executed
# This is here, that the wrapper gets executed.
f = wrappers[0]
debug.dbg('decorator end', f)
@@ -394,21 +390,20 @@ class Execution(Executable):
"""
Get the return vars of a function.
"""
#a = self.var_args; print '\n\n', a, a.values, a.parent_stmt
stmts = []
if isinstance(self.base, Class):
# there maybe executions of executions
# There maybe executions of executions.
stmts = [Instance(self.base, self.var_args)]
elif isinstance(self.base, Generator):
return self.base.execute()
else:
# don't do this with exceptions, as usual, because some deeper
# Don't do this with exceptions, as usual, because some deeper
# exceptions could be catched - and I wouldn't know what happened.
if hasattr(self.base, 'returns'):
stmts = self._get_function_returns(evaluate_generator)
else:
try:
# if it is an instance, we try to execute the __call__().
# If it is an instance, we try to execute the __call__().
call_method = self.base.get_subscope_by_name('__call__')
except (AttributeError, KeyError, DecoratorNotFound):
debug.warning("no execution possible", self.base)
@@ -458,14 +453,12 @@ class Execution(Executable):
new_param._assignment_calls = calls
name = copy.copy(param.get_name())
name.parent = new_param
#print 'insert', i, name, calls.values, value, self.base.params
return name
result = []
start_offset = 0
#print '\n\nfunc_params', self.base, self.base.parent, self.base
if isinstance(self.base, InstanceElement):
# care for self -> just exclude it and add the instance
# Care for self -> just exclude it and add the instance
start_offset = 1
self_name = copy.copy(self.base.params[0].get_name())
self_name.parent = self.base.instance
@@ -483,6 +476,9 @@ class Execution(Executable):
for param in self.base.params[start_offset:]:
# The value and key can both be null. There, the defaults apply.
# args / kwargs will just be empty arrays / dicts, respectively.
# Wrong value count is just ignored. If you try to test cases which
# are not allowed in Python, Jedi will maybe not show any
# completions.
key, value = next(var_arg_iterator, (None, None))
while key:
try:
@@ -495,8 +491,6 @@ class Execution(Executable):
key, value = next(var_arg_iterator, (None, None))
keys_only = True
#debug.warning('Too many arguments given.', value)
assignments = param.get_assignment_calls().values
assignment = assignments[0]
keys = []
@@ -508,7 +502,7 @@ class Execution(Executable):
if value:
values.append(value)
for key, value in var_arg_iterator:
# iterate until a key argument is found
# Iterate until a key argument is found.
if key:
var_arg_iterator.push_back(key, value)
break
@@ -523,11 +517,10 @@ class Execution(Executable):
if value:
values = [value]
else:
# just give it the default values (if there's something
# there)
# No value: return the default values.
values = assignments
# just ignore all the params that are without a key, after one
# Just ignore all the params that are without a key, after one
# keyword argument was set.
if not keys_only or assignment[0] == '**':
result.append(gen_param_name_copy(param, keys=keys,
@@ -539,7 +532,7 @@ class Execution(Executable):
Yields a key/value pair, the key is None, if its not a named arg.
"""
def iterate():
# var_args is typically an Array, and not a list
# `var_args` is typically an Array, and not a list.
for var_arg in self.var_args:
# *args
if var_arg[0] == '*':
@@ -552,17 +545,17 @@ class Execution(Executable):
arrays = follow_call_list([var_arg[1:]])
for array in arrays:
for key, field in array.get_contents():
# take the first index
# Take the first index.
if isinstance(key, parsing.Name):
name = key
else:
# parsing.[Call|Function|Class] lookup
# `parsing`.[Call|Function|Class] lookup.
name = key[0].name
yield name, field
# normal arguments (including key arguments)
# Normal arguments (including key arguments).
else:
if len(var_arg) > 1 and var_arg[1] == '=':
# this is a named parameter (var_arg[0] is a Call)
# This is a named parameter (var_arg[0] is a Call).
yield var_arg[0].name, var_arg[2:]
else:
yield None, var_arg
@@ -601,7 +594,7 @@ class Execution(Executable):
return self.get_params() + parsing.Scope._get_set_vars(self)
def copy_properties(self, prop):
# copy all these lists into this local function.
# Copy all these lists into this local function.
attr = getattr(self.base, prop)
objects = []
for element in attr:
@@ -652,12 +645,11 @@ class Generator(object):
"""
names = []
for n in ['__next__', 'send']:
# the name for the `next` function
# The name for the `next` function.
name = parsing.Name([n], 0, 0, 0)
name.parent = self
names.append(name)
for n in ['close', 'throw']:
# the name for the `next` function
name = parsing.Name([n], 0, 0, 0)
name.parent = None
names.append(name)
@@ -695,9 +687,9 @@ class Array(object):
return [self]
else:
# This is indexing only one element, with a fixed index number,
# otherwise it just ignores the index (e.g. [1+1])
# otherwise it just ignores the index (e.g. [1+1]).
try:
# multiple elements in the array
# Multiple elements in the array.
i = index.get_only_subelement().name
except AttributeError:
pass
@@ -713,7 +705,7 @@ class Array(object):
old_index = index
index = None
for i, key_elements in enumerate(self._array.keys):
# because we only want the key to be a string
# Because we only want the key to be a string.
if len(key_elements) == 1:
try:
str_key = key_elements.get_code()
@@ -739,7 +731,7 @@ class Array(object):
This method generates all ArrayElements for one parsing.Array.
It returns e.g. for a list: append, pop, ...
"""
# array.type is a string with the type, e.g. 'list'
# `array.type` is a string with the type, e.g. 'list'.
scope = get_scopes_for_name(builtin.Builtin.scope, self._array.type)[0]
names = scope.get_defined_names()
return [ArrayElement(n) for n in names]
@@ -769,7 +761,7 @@ class ArrayElement(object):
self.name = name
def __getattr__(self, name):
# set access rights:
# Set access privileges:
if name not in ['parent', 'names', 'line_nr', 'indent']:
raise AttributeError('Strange access: %s.' % name)
return getattr(self.name, name)
@@ -783,7 +775,7 @@ def get_defined_names_for_position(obj, position=None, start_scope=None):
:param position: the position as a row/column tuple, default is infinity.
"""
names = obj.get_defined_names()
# instances have special rules, always return all the possible completions,
# 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(obj, Instance) or isinstance(obj, Function) \
and isinstance(obj.decorated_func, Instance) \
@@ -806,7 +798,7 @@ def get_names_for_scope(scope, position=None, star_search=True):
start_scope = scope
while scope:
# `parsing.Class` is used, because the parent is never `Class`.
# ignore the Flows, because the classes and functions care for that.
# Ignore the Flows, because the classes and functions care for that.
if not (scope != start_scope and isinstance(scope, parsing.Class)
or isinstance(scope, parsing.Flow)
or isinstance(scope, InstanceElement)
@@ -818,13 +810,13 @@ def get_names_for_scope(scope, position=None, star_search=True):
raise MultiLevelStopIteration('StopIteration raised somewhere')
scope = scope.parent
# add star imports
# Add star imports.
if star_search:
for s in remove_star_imports(start_scope.get_parent_until()):
for g in get_names_for_scope(s, star_search=False):
yield g
# add builtins to the global scope
# Add builtins to the global scope.
builtin_scope = builtin.Builtin.scope
yield builtin_scope, builtin_scope.get_defined_names()
@@ -847,7 +839,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
if isinstance(r, parsing.Statement) \
or isinstance(r, InstanceElement) \
and isinstance(r.var, parsing.Statement):
# global variables handling
# Global variables handling.
if r.is_global():
for token_name in r.token_list[1:]:
if isinstance(token_name, parsing.Name):
@@ -871,9 +863,9 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
par = name.parent
if isinstance(par, parsing.Flow):
if par.command == 'for':
# take the first statement (for has always only
# Take the first statement (for has always only
# one, remember `in`). And follow it. After that,
# get the types which are in the array
# get the types which are in the array.
arrays = follow_statement(par.inits[0])
for array in arrays:
in_vars = array.get_index_types()
@@ -887,8 +879,8 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
elif isinstance(par, parsing.Param) \
and isinstance(par.parent.parent, parsing.Class) \
and par.position == 0:
# this is where self gets added - this happens at another
# place, if the var_args are clear. But some times the class is
# This is where self gets added - this happens at another
# place, if the var_args are clear. But sometimes the class is
# not known. Therefore add a new instance for self. Otherwise
# take the existing.
if isinstance(scope, InstanceElement):
@@ -899,10 +891,6 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
elif isinstance(par, (InstanceElement)) \
and hasattr(par, 'get_descriptor_return'):
try:
#print '\n\n'
#print name, par
#print par.get_descriptor_return(scope)
#raise KeyError()
result += par.get_descriptor_return(scope)
except KeyError:
result.append(par)
@@ -914,7 +902,6 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False):
# compare func uses the tuple of line/indent = row/column
comparison_func = lambda name: (name.line_nr, name.indent)
for scope, name_list in scope_generator:
#print scope, name_list[:9]
break_scopes = []
# here is the position stuff happening (sorting of variables)
for name in sorted(name_list, key=comparison_func, reverse=True):
@@ -956,7 +943,6 @@ def strip_imports(scopes):
result = []
for s in scopes:
if isinstance(s, parsing.Import):
#print 'dini mueter, steile griech!'
try:
result += follow_import(s)
except modules.ModuleNotFound:
@@ -991,20 +977,20 @@ def assign_tuples(tup, results, seek_name):
result = []
if tup.type == parsing.Array.NOARRAY:
# here we have unnessecary braces, which we just remove
# Here we have unnessecary braces, which we just remove.
arr = tup.get_only_subelement()
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,
# therefor we can just assume, that the first part is important.
# 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]
# check the left part, if it's still tuples in it or a Call
# Check the left part, if there are still tuples in it or a Call.
if isinstance(t, parsing.Array):
# these are "sub" tuples
# These are "sub"-tuples.
result += assign_tuples(t, eval_results(i), seek_name)
else:
if t.name.names[-1] == seek_name:
@@ -1030,10 +1016,10 @@ def follow_statement(stmt, seek_name=None):
# errors here must not be catched, because they shouldn't exist.
raise MultiLevelAttributeError(sys.exc_info())
# assignment checking is only important if the statement defines multiple
# variables
# Assignment checking is only important if the statement defines multiple
# variables.
if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details:
# TODO this should have its own call_list, because call_list can also
# TODO This should have its own call_list, because call_list can also
# return 3 results for 2 variables.
new_result = []
for op, set_vars in stmt.assignment_details:
@@ -1062,13 +1048,13 @@ def follow_call_list(call_list):
if parsing.Array.is_type(call, parsing.Array.NOARRAY):
result += follow_call_list(call)
else:
# with things like params, these can also be functions, etc
# With things like params, these can also be functions...
if isinstance(call, (Function, parsing.Class, Instance)):
result.append(call)
# The string tokens are just operations (+, -, etc.)
elif not isinstance(call, str):
# ternary operators
#if str(call.name) == 'for':
# Ternary operators.
#if str(call.name) == 'for': <--- list comprehensions
# print '\n\ndini mueter'
if str(call.name) == 'if':
while True:
@@ -1101,16 +1087,16 @@ def follow_call(call):
else:
debug.warning('unknown type:', current.type, current)
scopes = []
# make instances of those number/string objects
# Make instances of those number/string objects.
scopes = [Instance(s) for s in scopes]
else:
# this is the first global lookup
# This is the first global lookup.
scopes = get_scopes_for_name(scope, current, position=position,
search_global=True)
result = strip_imports(scopes)
if result != scopes:
# reset the position, when imports where stripped
# Reset the position, when imports where stripped.
position = None
debug.dbg('before next follow %s, current "%s", scope %s'
@@ -1133,7 +1119,7 @@ def follow_paths(path, results, position=None):
if fp is not None:
results_new += fp
else:
# this means stop iteration
# This means stop iteration.
return results
return results_new
@@ -1142,7 +1128,7 @@ def follow_path(path, scope, position=None):
"""
Takes a generator and tries to complete the path.
"""
# current is either an Array or a Scope
# Current is either an Array or a Scope.
try:
current = next(path)
except StopIteration:
@@ -1151,25 +1137,25 @@ def follow_path(path, scope, position=None):
result = []
if isinstance(current, parsing.Array):
# this must be an execution, either () or []
# This must be an execution, either () or [].
if current.type == parsing.Array.LIST:
result = scope.get_index_types(current)
elif current.type not in [parsing.Array.DICT]:
# scope must be a class or func - make an instance or execution
# Scope must be a class or func - make an instance or execution.
debug.dbg('exe', scope)
result = Execution(scope, current).get_return_types()
else:
# curly braces are not allowed, because they make no sense
# Curly braces are not allowed, because they make no sense.
debug.warning('strange function call with {}', current, scope)
else:
# the function must not be decorated with something else
# The function must not be decorated with something else.
if isinstance(scope, Function) and \
isinstance(scope.decorated_func, Function):
# TODO check default function methods and return them
# TODO Check default function methods and return them.
result = []
else:
# TODO check magic class methods and return them also
# this is the typical lookup while chaining things
# TODO Check magic class methods and return them also.
# This is the typical lookup while chaining things.
result = strip_imports(get_scopes_for_name(scope, current,
position=position))
return follow_paths(path, result, position=position)
@@ -1181,7 +1167,7 @@ def follow_import(_import):
:param _import: The import statement.
:type _import: parsing.Import
"""
# set path together
# Set path together.
ns_list = []
if _import.from_ns:
ns_list += _import.from_ns.names
@@ -1214,5 +1200,5 @@ def remove_star_imports(scope):
new += remove_star_imports(m)
modules += new
# filter duplicate modules
# Filter duplicate modules.
return set(modules)