From b8525c7e1e235ac99182057ad12f62a797043491 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 1 Jun 2014 11:24:24 +0200 Subject: [PATCH] get dicts partially working --- jedi/evaluate/param.py | 58 ++++++++++++++++++++------ test/static_analysis/star_arguments.py | 4 +- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/jedi/evaluate/param.py b/jedi/evaluate/param.py index 59002bbc..a9a67422 100644 --- a/jedi/evaluate/param.py +++ b/jedi/evaluate/param.py @@ -82,6 +82,7 @@ def get_params(evaluator, func, var_args): keys_used = set() keys_only = False va_values = None + had_multiple_value_error = False for param in 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. @@ -97,6 +98,7 @@ def get_params(evaluator, func, var_args): else: k = unicode(key) if k in keys_used: + had_multiple_value_error = True m = ("TypeError: %s() got multiple values for keyword argument '%s'." % (func.name, k)) analysis.add(evaluator, 'type-error-multiple-values', @@ -136,15 +138,13 @@ def get_params(evaluator, func, var_args): values = va_values else: if param.assignment_details: - # No value: return the default values. + # No value: Return the default values. has_default_value = True result.append(param.get_name()) # TODO is this allowed? it changes it long time. param.is_generated = True else: - # If there is no assignment detail, that means there is no - # assignment, just the result. Therefore nothing has to be - # returned. + # No value: Return an empty container values = [] if not keys_only and isinstance(var_args, pr.Array): calling_va = _get_calling_var_args(evaluator, var_args) @@ -167,6 +167,14 @@ def get_params(evaluator, func, var_args): for k in set(param_dict) - keys_used: result.append(_gen_param_name_copy(func, var_args, param_dict[k])) + if not non_matching_keys and not had_multiple_value_error: + # add a warning only if there's not another one. + calling_va = _get_calling_var_args(evaluator, var_args) + if calling_va is not None: + m = _error_argument_count(func, len(var_args)) + analysis.add(evaluator, 'type-error-too-few-arguments', + calling_va, message=m) + for key, va_values in non_matching_keys: m = "TypeError: %s() got an unexpected keyword argument '%s'." \ % (func.name, key) @@ -201,22 +209,21 @@ def _var_args_iterator(evaluator, var_args): continue # *args if expression_list[0] == '*': - # *args must be some sort of an array, otherwise -> ignore arrays = evaluator.eval_expression_list(expression_list[1:]) iterators = [_iterate_star_args(a) for a in arrays] for values in list(zip_longest(*iterators)): yield None, [v for v in values if v is not None] # **kwargs elif expression_list[0] == '**': + dct = {} for array in evaluator.eval_expression_list(expression_list[1:]): - if isinstance(array, iterable.Array): - for key_stmt, value_stmt in array.items(): - # first index, is the key if syntactically correct - call = key_stmt.expression_list()[0] - if isinstance(call, pr.Name): - yield call, [value_stmt] - elif isinstance(call, pr.Call): - yield call.name, [value_stmt] + for name, (key, value) in _star_star_dict(array).items(): + try: + dct[name][1].add(value) + except KeyError: + dct[name] = key, set([value]) + for key, values in dct.values(): + yield key, values # Normal arguments (including key arguments). else: if stmt.assignment_details: @@ -236,9 +243,34 @@ def _iterate_star_args(array): for field_stmt in array.iter_content(): yield helpers.FakeStatement([field_stmt]) else: + # *args must be some sort of an array, otherwise -> ignore pass # TODO need a warning here. +def _star_star_dict(array): + dct = {} + if isinstance(array, iterable.Array): + for key_stmt, value_stmt in array.items(): + # first index, is the key if syntactically correct + call = key_stmt.expression_list()[0] + if isinstance(call, pr.Name): + key = call + elif isinstance(call, pr.Call): + key = call.name + else: + continue + # raise warning + if str(key) not in dct: + dct[str(key)] = key, value_stmt + else: + pass + # raise warning + else: + pass + # raise warning + return dct + + def _gen_param_name_copy(func, var_args, param, keys=(), values=(), array_type=None): """ Create a param with the original scope (of varargs) as parent. diff --git a/test/static_analysis/star_arguments.py b/test/static_analysis/star_arguments.py index fb46bbe5..af851e30 100644 --- a/test/static_analysis/star_arguments.py +++ b/test/static_analysis/star_arguments.py @@ -38,10 +38,10 @@ nested_twice(2, 3, 4) def kwargs_test(**kwargs): - # TODO should not be here, but somewhere down there. - #! 14 type-error-multiple-values return simple2(1, **kwargs) kwargs_test(c=3, b=2) +#! 11 type-error-too-few-arguments kwargs_test(c=3) +#! 11 type-error-too-few-arguments kwargs_test(b=2)