closes bpo-27494: Fix 2to3 handling of trailing comma after a generator expression (#3771)
This commit is contained in:
parent
a8ed11742b
commit
af810b35b4
|
@ -130,8 +130,8 @@ atom: ('(' [yield_expr|testlist_gexp] ')' |
|
||||||
'{' [dictsetmaker] '}' |
|
'{' [dictsetmaker] '}' |
|
||||||
'`' testlist1 '`' |
|
'`' testlist1 '`' |
|
||||||
NAME | NUMBER | STRING+ | '.' '.' '.')
|
NAME | NUMBER | STRING+ | '.' '.' '.')
|
||||||
listmaker: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
listmaker: (test|star_expr) ( old_comp_for | (',' (test|star_expr))* [','] )
|
||||||
testlist_gexp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
testlist_gexp: (test|star_expr) ( old_comp_for | (',' (test|star_expr))* [','] )
|
||||||
lambdef: 'lambda' [varargslist] ':' test
|
lambdef: 'lambda' [varargslist] ':' test
|
||||||
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
||||||
subscriptlist: subscript (',' subscript)* [',']
|
subscriptlist: subscript (',' subscript)* [',']
|
||||||
|
@ -161,9 +161,28 @@ argument: ( test [comp_for] |
|
||||||
star_expr )
|
star_expr )
|
||||||
|
|
||||||
comp_iter: comp_for | comp_if
|
comp_iter: comp_for | comp_if
|
||||||
comp_for: [ASYNC] 'for' exprlist 'in' testlist_safe [comp_iter]
|
comp_for: [ASYNC] 'for' exprlist 'in' or_test [comp_iter]
|
||||||
comp_if: 'if' old_test [comp_iter]
|
comp_if: 'if' old_test [comp_iter]
|
||||||
|
|
||||||
|
# As noted above, testlist_safe extends the syntax allowed in list
|
||||||
|
# comprehensions and generators. We can't use it indiscriminately in all
|
||||||
|
# derivations using a comp_for-like pattern because the testlist_safe derivation
|
||||||
|
# contains comma which clashes with trailing comma in arglist.
|
||||||
|
#
|
||||||
|
# This was an issue because the parser would not follow the correct derivation
|
||||||
|
# when parsing syntactically valid Python code. Since testlist_safe was created
|
||||||
|
# specifically to handle list comprehensions and generator expressions enclosed
|
||||||
|
# with parentheses, it's safe to only use it in those. That avoids the issue; we
|
||||||
|
# can parse code like set(x for x in [],).
|
||||||
|
#
|
||||||
|
# The syntax supported by this set of rules is not a valid Python 3 syntax,
|
||||||
|
# hence the prefix "old".
|
||||||
|
#
|
||||||
|
# See https://bugs.python.org/issue27494
|
||||||
|
old_comp_iter: old_comp_for | old_comp_if
|
||||||
|
old_comp_for: [ASYNC] 'for' exprlist 'in' testlist_safe [old_comp_iter]
|
||||||
|
old_comp_if: 'if' old_test [old_comp_iter]
|
||||||
|
|
||||||
testlist1: test (',' test)*
|
testlist1: test (',' test)*
|
||||||
|
|
||||||
# not used in grammar, but may appear in "node" passed from Parser to Compiler
|
# not used in grammar, but may appear in "node" passed from Parser to Compiler
|
||||||
|
|
|
@ -101,8 +101,8 @@ def ListComp(xp, fp, it, test=None):
|
||||||
test.prefix = " "
|
test.prefix = " "
|
||||||
if_leaf = Leaf(token.NAME, "if")
|
if_leaf = Leaf(token.NAME, "if")
|
||||||
if_leaf.prefix = " "
|
if_leaf.prefix = " "
|
||||||
inner_args.append(Node(syms.comp_if, [if_leaf, test]))
|
inner_args.append(Node(syms.old_comp_if, [if_leaf, test]))
|
||||||
inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
|
inner = Node(syms.listmaker, [xp, Node(syms.old_comp_for, inner_args)])
|
||||||
return Node(syms.atom,
|
return Node(syms.atom,
|
||||||
[Leaf(token.LBRACE, "["),
|
[Leaf(token.LBRACE, "["),
|
||||||
inner,
|
inner,
|
||||||
|
@ -208,7 +208,7 @@ def attr_chain(obj, attr):
|
||||||
next = getattr(next, attr)
|
next = getattr(next, attr)
|
||||||
|
|
||||||
p0 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
p0 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
||||||
| comp_for< 'for' any 'in' node=any any* >
|
| old_comp_for< 'for' any 'in' node=any any* >
|
||||||
"""
|
"""
|
||||||
p1 = """
|
p1 = """
|
||||||
power<
|
power<
|
||||||
|
|
|
@ -83,7 +83,7 @@ class FixDict(fixer_base.BaseFix):
|
||||||
p1 = patcomp.compile_pattern(P1)
|
p1 = patcomp.compile_pattern(P1)
|
||||||
|
|
||||||
P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
||||||
| comp_for< 'for' any 'in' node=any any* >
|
| old_comp_for< 'for' any 'in' node=any any* >
|
||||||
"""
|
"""
|
||||||
p2 = patcomp.compile_pattern(P2)
|
p2 = patcomp.compile_pattern(P2)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ class FixParen(fixer_base.BaseFix):
|
||||||
PATTERN = """
|
PATTERN = """
|
||||||
atom< ('[' | '(')
|
atom< ('[' | '(')
|
||||||
(listmaker< any
|
(listmaker< any
|
||||||
comp_for<
|
old_comp_for<
|
||||||
'for' NAME 'in'
|
'for' NAME 'in'
|
||||||
target=testlist_safe< any (',' any)+ [',']
|
target=testlist_safe< any (',' any)+ [',']
|
||||||
>
|
>
|
||||||
|
@ -24,7 +24,7 @@ class FixParen(fixer_base.BaseFix):
|
||||||
>
|
>
|
||||||
|
|
|
|
||||||
testlist_gexp< any
|
testlist_gexp< any
|
||||||
comp_for<
|
old_comp_for<
|
||||||
'for' NAME 'in'
|
'for' NAME 'in'
|
||||||
target=testlist_safe< any (',' any)+ [',']
|
target=testlist_safe< any (',' any)+ [',']
|
||||||
>
|
>
|
||||||
|
|
|
@ -55,7 +55,7 @@ class FixXrange(fixer_base.BaseFix):
|
||||||
p1 = patcomp.compile_pattern(P1)
|
p1 = patcomp.compile_pattern(P1)
|
||||||
|
|
||||||
P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
|
||||||
| comp_for< 'for' any 'in' node=any any* >
|
| old_comp_for< 'for' any 'in' node=any any* >
|
||||||
| comparison< any 'in' node=any any*>
|
| comparison< any 'in' node=any any*>
|
||||||
"""
|
"""
|
||||||
p2 = patcomp.compile_pattern(P2)
|
p2 = patcomp.compile_pattern(P2)
|
||||||
|
|
|
@ -459,6 +459,13 @@ class TestLiterals(GrammarTest):
|
||||||
self.validate(s)
|
self.validate(s)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGeneratorExpressions(GrammarTest):
|
||||||
|
|
||||||
|
def test_trailing_comma_after_generator_expression_argument_works(self):
|
||||||
|
# BPO issue 27494
|
||||||
|
self.validate("set(x for x in [],)")
|
||||||
|
|
||||||
|
|
||||||
def diff(fn, result):
|
def diff(fn, result):
|
||||||
try:
|
try:
|
||||||
with open('@', 'w') as f:
|
with open('@', 'w') as f:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Make 2to3 accept a trailing comma in generator expressions. For example, ``set(x
|
||||||
|
for x in [],)`` is now allowed.
|
Loading…
Reference in New Issue