1
0
forked from VimPlug/jedi

*args / **kwargs remodelled

This commit is contained in:
David Halter
2012-05-07 22:41:41 +02:00
parent 40e59c0476
commit 57128bf17f
6 changed files with 131 additions and 72 deletions

View File

@@ -3,5 +3,14 @@
try:
next = next
except NameError:
def next(obj):
return obj.next()
_raiseStopIteration = object()
def next(iterator, default=_raiseStopIteration):
if not hasattr(iterator, 'next'):
raise TypeError("not an iterator")
try:
return iterator.next()
except StopIteration:
if default is _raiseStopIteration:
raise
else:
return default

View File

@@ -25,12 +25,16 @@ import builtin
memoize_caches = []
class MultiLevelStopIteration(Exception):
pass
def clear_caches():
for m in memoize_caches:
m.clear()
def memoize(default=None):
def memoize_default(default=None):
"""
This is a typical memoization decorator, BUT there is one difference:
To prevent recursion it sets defaults.
@@ -78,7 +82,7 @@ class Executable(object):
pass
raise IndexError('No params available')
@memoize(default=[])
@memoize_default(default=[])
def get_params(self):
"""
This returns the params for an Execution/Instance and is injected as a
@@ -86,6 +90,21 @@ class Executable(object):
This needs to be here, because Instance can have __init__ functions,
which act the same way as normal functions
"""
def gen_param_name_copy(param, keys=[], values=[], array_type=None):
calls = parsing.Array(parsing.Array.NOARRAY,
self.var_args.parent_stmt)
calls.values = values
calls.keys = keys
print 'arr_t', array_type
calls.type = array_type
new_param = copy.copy(param)
new_param._assignment_calls_calculated = True
new_param._assignment_calls = calls
name = copy.copy(param.get_name())
name.parent = new_param
#print 'insert', i, name, calls.values, value, self.func.params
return name
result = []
start_offset = 0
#print '\n\nfunc_params', self.func, self.func.parent, self.func
@@ -104,54 +123,56 @@ class Executable(object):
# There may be calls, which don't fit all the params, this just ignores
# it.
var_arg_iterator = self.get_var_args_iterator()
for i, (key, value) in enumerate(var_arg_iterator, start_offset):
param = None
try:
param = param_dict[key]
except:
try:
param = self.func.params[i]
except IndexError:
debug.warning('Too many arguments given.', value)
if param:
calls = parsing.Array(parsing.Array.NOARRAY,
self.var_args.parent_stmt)
non_matching_keys = []
for param in self.func.params[start_offset:]:
# The value and key can both be null. There, the defaults apply.
# args / kwargs will just be empty arrays / dicts, respectively.
key, value = next(var_arg_iterator, (None, None))
print '\n\nlala', key, value
while key:
try:
key_param = param_dict[key]
except IndexError:
non_matching_keys.append((key, value))
else:
result.append(gen_param_name_copy(key_param,
values=[value]))
key, value = next(var_arg_iterator, (None, None))
#debug.warning('Too many arguments given.', value)
assignment = param.get_assignment_calls().values[0]
keys = []
values = []
array_type = None
if assignment[0] == '*':
# *args param
print '\n\n3*', value, assignment
calls.type = parsing.Array.TUPLE
if key:
var_arg_iterator.push_back(key, value)
else:
calls.values = [value]
array_type = parsing.Array.TUPLE
if value:
values.append(value)
for key, value in var_arg_iterator:
# iterate until a key argument is found
if key:
var_arg_iterator.push_back(key, value)
break
calls.values.append(value)
values.append(value)
elif assignment[0] == '**':
# **kwargs param
calls.type = parsing.Array.DICT
calls.values = [value]
calls.keys = [key]
for value in var_arg_iterator:
calls.values.append(value)
calls.keys.append(key)
# it is a **args param
print '\n\n**', key, value, assignment
array_type = parsing.Array.DICT
if non_matching_keys:
keys, values = zip(*non_matching_keys)
print '\n\n**', keys, values, assignment
else:
# normal param
calls.values = [value]
print 'normal', value
if value:
values = [value]
new_param = copy.copy(param)
new_param._assignment_calls = calls
name = copy.copy(param.get_name())
name.parent = new_param
#print 'insert', i, name, calls.values, value, self.func.params
result.append(name)
result.append(gen_param_name_copy(param, keys=keys, values=values,
array_type=array_type))
return result
def get_var_args_iterator(self):
@@ -345,7 +366,7 @@ class Execution(Executable):
"""
cache = {}
@memoize(default=[])
@memoize_default(default=[])
def get_return_types(self):
"""
Get the return vars of a function.
@@ -465,7 +486,10 @@ def get_names_for_scope(scope, position=None, star_search=True):
# class variables/functions are only available
if (not isinstance(scope, Class) or scope == start_scope) \
and not isinstance(scope, parsing.Flow):
try:
yield scope, get_defined_names_for_position(scope, position)
except StopIteration:
raise MultiLevelStopIteration('StopIteration raised somewhere')
scope = scope.parent
# add star imports
@@ -638,7 +662,7 @@ def assign_tuples(tup, results, seek_name):
return result
@memoize(default=[])
@memoize_default(default=[])
def follow_statement(stmt, scope=None, seek_name=None):
"""
:param stmt: contains a statement
@@ -673,9 +697,12 @@ def follow_call_list(scope, call_list):
It is used to evaluate a two dimensional object, that has calls, arrays and
operators in it.
"""
if parsing.Array.is_type(call_list, parsing.Array.TUPLE):
print 'inpu', scope, call_list
if parsing.Array.is_type(call_list, parsing.Array.TUPLE,
parsing.Array.DICT):
# Tuples can stand just alone without any braces. These would be
# recognized as separate calls, but actually are a tuple.
print 'inpu', scope, call_list
result = follow_call(scope, call_list)
else:
result = []
@@ -729,15 +756,18 @@ def follow_call(scope, call):
def follow_paths(path, results, position=None):
results_new = []
try:
if results:
if len(results) > 1:
iter_paths = itertools.tee(path, len(results))
else:
iter_paths = [path]
for i, r in enumerate(results):
results_new += follow_path(iter_paths[i], r, position=position)
except StopIteration:
fp = follow_path(iter_paths[i], r, position=position)
if fp is not None:
results_new += fp
else:
# this means stop iteration
return results
return results_new
@@ -747,7 +777,10 @@ def follow_path(path, scope, position=None):
Takes a generator and tries to complete the path.
"""
# current is either an Array or a Scope
try:
current = next(path)
except StopIteration:
return None
debug.dbg('follow', current, scope)
result = []

View File

@@ -205,6 +205,7 @@ def complete(source, row, column, source_path):
for s in scopes:
# TODO is this reall the right way? just ignore the functions? \
# do the magic functions first? and then recheck here?
print 'scope, '
if not isinstance(s, parsing.Function):
completions += s.get_defined_names()

View File

@@ -160,10 +160,6 @@ a.
class C(object):
def c_a(self):
self.c_b = 1
@@ -171,10 +167,14 @@ class C(object):
test = [1,2]
def args_func(arg1, *args, **kwargs):
return (arg1, args)
def args_func(arg1, arg2=1, *args, **kwargs):
return arg1, arg2, args, kwargs
exe = args_func(list,"", 3)
args_func(1,"", a=list)[0].
exe = args_func(1,"")
args_func() #1,"", a=list)[0].
args_func(arg1=0, *test + [3], *[4,5], **{'a': 'b'}).
exe[1].
exe[3].

View File

@@ -325,7 +325,7 @@ class Function(Scope):
def get_set_vars(self):
n = super(Function, self).get_set_vars()
if self.param_cb or False:
if self.param_cb:
# this is the really ugly part, where the functional style of this
# get methods is broken, it executes a callback.
# This is important, because something has to inject the params
@@ -533,6 +533,8 @@ class Statement(Simple):
# cache
self._assignment_calls = None
self._assignment_details = None
# this is important for other scripts
self._assignment_calls_calculated = False
def get_code(self, new_line=True):
if new_line:
@@ -566,7 +568,7 @@ class Statement(Simple):
This is not really nice written, sorry for that. If you plan to replace
it and make it nicer, that would be cool :-)
"""
if self._assignment_calls:
if self._assignment_calls_calculated:
return self._assignment_calls
self._assignment_details = []
result = Array(Array.NOARRAY, self)
@@ -827,13 +829,13 @@ class Array(Call):
return self.values[0][0]
@staticmethod
def is_type(instance, typ):
def is_type(instance, *types):
"""
This is not only used for calls on the actual object, but for
ducktyping, to invoke this function with anything as `self`.
"""
if isinstance(instance, Array):
if instance.type == typ:
if instance.type in types:
return True
return False

View File

@@ -109,7 +109,21 @@ exe[1][1].append
# -----------------
# ** kwargs
# -----------------
def kwargs_func(**kwargs):
return kwargs
exe = kwargs_func(a=3,b=4)
#? []
exe.
# -----------------
# *args / ** kwargs
# -----------------
def fu(a=1, b="", *args, **kwargs):
return a,b,args,kwargs
exe = fu(list, 1, "", c=set)
##? ['append']
exe[0].