mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 14:34:31 +08:00
*args / **kwargs remodelled
This commit is contained in:
@@ -3,5 +3,14 @@
|
|||||||
try:
|
try:
|
||||||
next = next
|
next = next
|
||||||
except NameError:
|
except NameError:
|
||||||
def next(obj):
|
_raiseStopIteration = object()
|
||||||
return obj.next()
|
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
|
||||||
|
|||||||
115
evaluate.py
115
evaluate.py
@@ -25,12 +25,16 @@ import builtin
|
|||||||
memoize_caches = []
|
memoize_caches = []
|
||||||
|
|
||||||
|
|
||||||
|
class MultiLevelStopIteration(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def clear_caches():
|
def clear_caches():
|
||||||
for m in memoize_caches:
|
for m in memoize_caches:
|
||||||
m.clear()
|
m.clear()
|
||||||
|
|
||||||
|
|
||||||
def memoize(default=None):
|
def memoize_default(default=None):
|
||||||
"""
|
"""
|
||||||
This is a typical memoization decorator, BUT there is one difference:
|
This is a typical memoization decorator, BUT there is one difference:
|
||||||
To prevent recursion it sets defaults.
|
To prevent recursion it sets defaults.
|
||||||
@@ -78,7 +82,7 @@ class Executable(object):
|
|||||||
pass
|
pass
|
||||||
raise IndexError('No params available')
|
raise IndexError('No params available')
|
||||||
|
|
||||||
@memoize(default=[])
|
@memoize_default(default=[])
|
||||||
def get_params(self):
|
def get_params(self):
|
||||||
"""
|
"""
|
||||||
This returns the params for an Execution/Instance and is injected as a
|
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,
|
This needs to be here, because Instance can have __init__ functions,
|
||||||
which act the same way as normal 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 = []
|
result = []
|
||||||
start_offset = 0
|
start_offset = 0
|
||||||
#print '\n\nfunc_params', self.func, self.func.parent, self.func
|
#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
|
# There may be calls, which don't fit all the params, this just ignores
|
||||||
# it.
|
# it.
|
||||||
var_arg_iterator = self.get_var_args_iterator()
|
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:
|
non_matching_keys = []
|
||||||
calls = parsing.Array(parsing.Array.NOARRAY,
|
for param in self.func.params[start_offset:]:
|
||||||
self.var_args.parent_stmt)
|
# 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]
|
assignment = param.get_assignment_calls().values[0]
|
||||||
|
keys = []
|
||||||
|
values = []
|
||||||
|
array_type = None
|
||||||
if assignment[0] == '*':
|
if assignment[0] == '*':
|
||||||
# *args param
|
# *args param
|
||||||
print '\n\n3*', value, assignment
|
print '\n\n3*', value, assignment
|
||||||
calls.type = parsing.Array.TUPLE
|
|
||||||
if key:
|
array_type = parsing.Array.TUPLE
|
||||||
var_arg_iterator.push_back(key, value)
|
if value:
|
||||||
else:
|
values.append(value)
|
||||||
calls.values = [value]
|
|
||||||
for key, value in var_arg_iterator:
|
for key, value in var_arg_iterator:
|
||||||
|
# iterate until a key argument is found
|
||||||
if key:
|
if key:
|
||||||
var_arg_iterator.push_back(key, value)
|
var_arg_iterator.push_back(key, value)
|
||||||
break
|
break
|
||||||
calls.values.append(value)
|
values.append(value)
|
||||||
elif assignment[0] == '**':
|
elif assignment[0] == '**':
|
||||||
# **kwargs param
|
# **kwargs param
|
||||||
calls.type = parsing.Array.DICT
|
array_type = parsing.Array.DICT
|
||||||
calls.values = [value]
|
if non_matching_keys:
|
||||||
calls.keys = [key]
|
keys, values = zip(*non_matching_keys)
|
||||||
for value in var_arg_iterator:
|
print '\n\n**', keys, values, assignment
|
||||||
calls.values.append(value)
|
|
||||||
calls.keys.append(key)
|
|
||||||
# it is a **args param
|
|
||||||
print '\n\n**', key, value, assignment
|
|
||||||
else:
|
else:
|
||||||
# normal param
|
# normal param
|
||||||
calls.values = [value]
|
print 'normal', value
|
||||||
|
if value:
|
||||||
|
values = [value]
|
||||||
|
|
||||||
new_param = copy.copy(param)
|
result.append(gen_param_name_copy(param, keys=keys, values=values,
|
||||||
new_param._assignment_calls = calls
|
array_type=array_type))
|
||||||
name = copy.copy(param.get_name())
|
|
||||||
name.parent = new_param
|
|
||||||
#print 'insert', i, name, calls.values, value, self.func.params
|
|
||||||
result.append(name)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_var_args_iterator(self):
|
def get_var_args_iterator(self):
|
||||||
@@ -345,7 +366,7 @@ class Execution(Executable):
|
|||||||
"""
|
"""
|
||||||
cache = {}
|
cache = {}
|
||||||
|
|
||||||
@memoize(default=[])
|
@memoize_default(default=[])
|
||||||
def get_return_types(self):
|
def get_return_types(self):
|
||||||
"""
|
"""
|
||||||
Get the return vars of a function.
|
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
|
# class variables/functions are only available
|
||||||
if (not isinstance(scope, Class) or scope == start_scope) \
|
if (not isinstance(scope, Class) or scope == start_scope) \
|
||||||
and not isinstance(scope, parsing.Flow):
|
and not isinstance(scope, parsing.Flow):
|
||||||
|
try:
|
||||||
yield scope, get_defined_names_for_position(scope, position)
|
yield scope, get_defined_names_for_position(scope, position)
|
||||||
|
except StopIteration:
|
||||||
|
raise MultiLevelStopIteration('StopIteration raised somewhere')
|
||||||
scope = scope.parent
|
scope = scope.parent
|
||||||
|
|
||||||
# add star imports
|
# add star imports
|
||||||
@@ -638,7 +662,7 @@ def assign_tuples(tup, results, seek_name):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@memoize(default=[])
|
@memoize_default(default=[])
|
||||||
def follow_statement(stmt, scope=None, seek_name=None):
|
def follow_statement(stmt, scope=None, seek_name=None):
|
||||||
"""
|
"""
|
||||||
:param stmt: contains a statement
|
: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
|
It is used to evaluate a two dimensional object, that has calls, arrays and
|
||||||
operators in it.
|
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
|
# Tuples can stand just alone without any braces. These would be
|
||||||
# recognized as separate calls, but actually are a tuple.
|
# recognized as separate calls, but actually are a tuple.
|
||||||
|
print 'inpu', scope, call_list
|
||||||
result = follow_call(scope, call_list)
|
result = follow_call(scope, call_list)
|
||||||
else:
|
else:
|
||||||
result = []
|
result = []
|
||||||
@@ -729,15 +756,18 @@ def follow_call(scope, call):
|
|||||||
|
|
||||||
def follow_paths(path, results, position=None):
|
def follow_paths(path, results, position=None):
|
||||||
results_new = []
|
results_new = []
|
||||||
try:
|
|
||||||
if results:
|
if results:
|
||||||
if len(results) > 1:
|
if len(results) > 1:
|
||||||
iter_paths = itertools.tee(path, len(results))
|
iter_paths = itertools.tee(path, len(results))
|
||||||
else:
|
else:
|
||||||
iter_paths = [path]
|
iter_paths = [path]
|
||||||
|
|
||||||
for i, r in enumerate(results):
|
for i, r in enumerate(results):
|
||||||
results_new += follow_path(iter_paths[i], r, position=position)
|
fp = follow_path(iter_paths[i], r, position=position)
|
||||||
except StopIteration:
|
if fp is not None:
|
||||||
|
results_new += fp
|
||||||
|
else:
|
||||||
|
# this means stop iteration
|
||||||
return results
|
return results
|
||||||
return results_new
|
return results_new
|
||||||
|
|
||||||
@@ -747,7 +777,10 @@ def follow_path(path, scope, position=None):
|
|||||||
Takes a generator and tries to complete the path.
|
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)
|
current = next(path)
|
||||||
|
except StopIteration:
|
||||||
|
return None
|
||||||
debug.dbg('follow', current, scope)
|
debug.dbg('follow', current, scope)
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ def complete(source, row, column, source_path):
|
|||||||
for s in scopes:
|
for s in scopes:
|
||||||
# TODO is this reall the right way? just ignore the functions? \
|
# TODO is this reall the right way? just ignore the functions? \
|
||||||
# do the magic functions first? and then recheck here?
|
# do the magic functions first? and then recheck here?
|
||||||
|
print 'scope, '
|
||||||
if not isinstance(s, parsing.Function):
|
if not isinstance(s, parsing.Function):
|
||||||
completions += s.get_defined_names()
|
completions += s.get_defined_names()
|
||||||
|
|
||||||
|
|||||||
18
parsetest.py
18
parsetest.py
@@ -160,10 +160,6 @@ a.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class C(object):
|
class C(object):
|
||||||
def c_a(self):
|
def c_a(self):
|
||||||
self.c_b = 1
|
self.c_b = 1
|
||||||
@@ -171,10 +167,14 @@ class C(object):
|
|||||||
test = [1,2]
|
test = [1,2]
|
||||||
|
|
||||||
|
|
||||||
def args_func(arg1, *args, **kwargs):
|
def args_func(arg1, arg2=1, *args, **kwargs):
|
||||||
return (arg1, args)
|
return arg1, arg2, args, kwargs
|
||||||
|
|
||||||
exe = args_func(list,"", 3)
|
exe = args_func(1,"")
|
||||||
args_func(1,"", a=list)[0].
|
args_func() #1,"", a=list)[0].
|
||||||
args_func(arg1=0, *test + [3], *[4,5], **{'a': 'b'}).
|
args_func(arg1=0, *test + [3], *[4,5], **{'a': 'b'}).
|
||||||
exe[1].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exe[3].
|
||||||
|
|||||||
10
parsing.py
10
parsing.py
@@ -325,7 +325,7 @@ class Function(Scope):
|
|||||||
|
|
||||||
def get_set_vars(self):
|
def get_set_vars(self):
|
||||||
n = super(Function, self).get_set_vars()
|
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
|
# this is the really ugly part, where the functional style of this
|
||||||
# get methods is broken, it executes a callback.
|
# get methods is broken, it executes a callback.
|
||||||
# This is important, because something has to inject the params
|
# This is important, because something has to inject the params
|
||||||
@@ -533,6 +533,8 @@ class Statement(Simple):
|
|||||||
# cache
|
# cache
|
||||||
self._assignment_calls = None
|
self._assignment_calls = None
|
||||||
self._assignment_details = None
|
self._assignment_details = None
|
||||||
|
# this is important for other scripts
|
||||||
|
self._assignment_calls_calculated = False
|
||||||
|
|
||||||
def get_code(self, new_line=True):
|
def get_code(self, new_line=True):
|
||||||
if new_line:
|
if new_line:
|
||||||
@@ -566,7 +568,7 @@ class Statement(Simple):
|
|||||||
This is not really nice written, sorry for that. If you plan to replace
|
This is not really nice written, sorry for that. If you plan to replace
|
||||||
it and make it nicer, that would be cool :-)
|
it and make it nicer, that would be cool :-)
|
||||||
"""
|
"""
|
||||||
if self._assignment_calls:
|
if self._assignment_calls_calculated:
|
||||||
return self._assignment_calls
|
return self._assignment_calls
|
||||||
self._assignment_details = []
|
self._assignment_details = []
|
||||||
result = Array(Array.NOARRAY, self)
|
result = Array(Array.NOARRAY, self)
|
||||||
@@ -827,13 +829,13 @@ class Array(Call):
|
|||||||
return self.values[0][0]
|
return self.values[0][0]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_type(instance, typ):
|
def is_type(instance, *types):
|
||||||
"""
|
"""
|
||||||
This is not only used for calls on the actual object, but for
|
This is not only used for calls on the actual object, but for
|
||||||
ducktyping, to invoke this function with anything as `self`.
|
ducktyping, to invoke this function with anything as `self`.
|
||||||
"""
|
"""
|
||||||
if isinstance(instance, Array):
|
if isinstance(instance, Array):
|
||||||
if instance.type == typ:
|
if instance.type in types:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,21 @@ exe[1][1].append
|
|||||||
# -----------------
|
# -----------------
|
||||||
# ** kwargs
|
# ** kwargs
|
||||||
# -----------------
|
# -----------------
|
||||||
|
def kwargs_func(**kwargs):
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
exe = kwargs_func(a=3,b=4)
|
||||||
|
#? []
|
||||||
|
exe.
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
# *args / ** kwargs
|
# *args / ** kwargs
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
||||||
|
def fu(a=1, b="", *args, **kwargs):
|
||||||
|
return a,b,args,kwargs
|
||||||
|
|
||||||
|
exe = fu(list, 1, "", c=set)
|
||||||
|
|
||||||
|
##? ['append']
|
||||||
|
exe[0].
|
||||||
|
|||||||
Reference in New Issue
Block a user