From babd7fca92b04d6f3e1a91a66ae0a7e4adb6a314 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 25 Jul 2017 01:54:25 +0200 Subject: [PATCH] Add issue 'positional argument follows keyword argument unpacking'. --- parso/python/normalizer.py | 57 ++++++++++++++++++++++++++------------ test/test_python_errors.py | 1 + 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/parso/python/normalizer.py b/parso/python/normalizer.py index b0c24c4..34ccfbc 100644 --- a/parso/python/normalizer.py +++ b/parso/python/normalizer.py @@ -176,25 +176,46 @@ class ErrorFinder(Normalizer): elif node.type == 'arglist': first_arg = node.children[0] if first_arg.type == 'argument' \ - and first_arg.children[1].type == 'comp_for' \ - and len(node.children) >= 2: - # foo(x for x in [], b) - message = "Generator expression must be parenthesized if not sole argument" - self._add_syntax_error(message, node) + and first_arg.children[1].type == 'comp_for': + if len(node.children) >= 2: + # foo(x for x in [], b) + message = "Generator expression must be parenthesized if not sole argument" + self._add_syntax_error(message, node) + else: + arg_set = set() + star_count = 0 + kw_only = False + kw_unpacking_only = False + # In python 3 this would be a bit easier (stars are part of + # argument), but we have to understand both. + for argument in node.children: + if argument == ',': + star_count = 0 + continue + if argument == '*': + star_count = len(argument.value) + continue - ### - arg_set = set() - for argument in node.children[::2]: - if argument.type == 'argument' and argument.children[1] == '=': - kw_only = True - keyword = argument.children[0] - if keyword.type == 'name': - if keyword.value in arg_set: - # f(x=1, x=2) - message = "keyword argument repeated" - self._add_syntax_error(message, keyword) - else: - arg_set.add(keyword.value) + if argument.type == 'argument': + first = argument.children[0] + if first in ('*', '**'): + star_count = len(argument.children[0].value) + if first == '**': + kw_unpacking_only = True + else: # Is a keyword argument. + kw_only = True + if first.type == 'name': + if first.value in arg_set: + # f(x=1, x=2) + message = "keyword argument repeated" + self._add_syntax_error(message, first) + else: + arg_set.add(first.value) + else: + if kw_unpacking_only: + # f(**x, y) >= 3.5 + message = "positional argument follows keyword argument unpacking" + self._add_syntax_error(message, argument) elif node.type == 'atom': first = node.children[0] # e.g. 's' b'' diff --git a/test/test_python_errors.py b/test/test_python_errors.py index f3da805..7da3473 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -93,6 +93,7 @@ def test_indentation_errors(code, positions): 'foo(+a=3)', 'f(lambda: 1=1)', 'f(x=1, x=2)', + 'f(**x, y)', # IndentationError ' foo',