From 3fb50c3ca5970eb268f8b8ce9e458aca412fe41f Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Fri, 5 Apr 2019 18:31:12 -0700 Subject: [PATCH] lib2to3: Support complex expressions in *args and **kwargs. There are two copies of the grammar -- the one used by Python itself as Grammar/Grammar, and the one used by lib2to3 which has necessarily diverged at Lib/lib2to3/Grammar.txt because it needs to support older syntax and we want it to be reasonably stable to avoid requiring fixer rewrites. This brings support for boolean expressions to `*args` and `**kwargs` to match what the live Python grammar does. Unfortunately, this means changes to the parse tree, so three included fixers are adjusted to support either way. Note that blib2to3 (black's fork) has commit 5192ed484bdbe507a8dd03dc31f93e4efec95b19 which solved this (albeit with different tests) locally. --- Lib/lib2to3/Grammar.txt | 4 ++-- Lib/lib2to3/fixes/fix_apply.py | 2 +- Lib/lib2to3/fixes/fix_intern.py | 2 +- Lib/lib2to3/fixes/fix_reload.py | 2 +- Lib/lib2to3/tests/test_parser.py | 14 ++++++++++++++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt index a7ddad3cf322c5f..2519fe572a6af50 100644 --- a/Lib/lib2to3/Grammar.txt +++ b/Lib/lib2to3/Grammar.txt @@ -138,8 +138,8 @@ arglist: argument (',' argument)* [','] # that precede iterable unpackings are blocked; etc. argument: ( test [comp_for] | test '=' test | - '**' expr | - star_expr ) + '**' test | + '*' test ) comp_iter: comp_for | comp_if comp_for: [ASYNC] 'for' exprlist 'in' testlist_safe [comp_iter] diff --git a/Lib/lib2to3/fixes/fix_apply.py b/Lib/lib2to3/fixes/fix_apply.py index 826ec8c9b62abbb..6d9dae6fab98029 100644 --- a/Lib/lib2to3/fixes/fix_apply.py +++ b/Lib/lib2to3/fixes/fix_apply.py @@ -40,7 +40,7 @@ def transform(self, node, results): if args.type == self.syms.star_expr: return # Make no change. if (args.type == self.syms.argument and - args.children[0].value == '**'): + args.children[0].value in ('*', '**')): return # Make no change. if kwds and (kwds.type == self.syms.argument and kwds.children[0].value == '**'): diff --git a/Lib/lib2to3/fixes/fix_intern.py b/Lib/lib2to3/fixes/fix_intern.py index a852330908bd9f4..cf11acde89b69a6 100644 --- a/Lib/lib2to3/fixes/fix_intern.py +++ b/Lib/lib2to3/fixes/fix_intern.py @@ -33,7 +33,7 @@ def transform(self, node, results): if obj.type == self.syms.star_expr: return # Make no change. if (obj.type == self.syms.argument and - obj.children[0].value == '**'): + obj.children[0].value in ('*', '**')): return # Make no change. names = ('sys', 'intern') new = ImportAndCall(node, results, names) diff --git a/Lib/lib2to3/fixes/fix_reload.py b/Lib/lib2to3/fixes/fix_reload.py index 6c7fbbd3be3fb3b..1ea9298021f6268 100644 --- a/Lib/lib2to3/fixes/fix_reload.py +++ b/Lib/lib2to3/fixes/fix_reload.py @@ -30,7 +30,7 @@ def transform(self, node, results): if obj.type == self.syms.star_expr: return # Make no change. if (obj.type == self.syms.argument and - obj.children[0].value == '**'): + obj.children[0].value in ('*', '**')): return # Make no change. names = ('importlib', 'reload') new = ImportAndCall(node, results, names) diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 01b2b51e4ab6c1a..995a1794ca0e1f5 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -622,6 +622,20 @@ def test_multiline_str_literals(self): self.validate(s) +class TestStarArgumentsExpr(GrammarTest): + + def test_arg_expressions(self): + driver.parse_string("f(*x)\n") + driver.parse_string("f(*x or [1])\n") + driver.parse_string("f(*x if x else [1])\n") + + def test_kwarg_expressions(self): + driver.parse_string("f(**x)\n") + driver.parse_string("f(**not x)\n") + driver.parse_string("f(**x or {})\n") + driver.parse_string("f(**x if x else {})\n") + + class TestPickleableException(unittest.TestCase): def test_ParseError(self): err = ParseError('msg', 2, None, (1, 'context'))