lib2to3: Support named assignment expressions (GH-12702)
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 an we want it to be reasonable stable to avoid requiring fixer rewrites. This brings suport for syntax like `if x:= foo():` to match what the live Python grammar does. This should've been added at the time of the walrus operator itself, but lib2to3 being independent is often overlooked. So we do consider this a bugfix rather than enhancement.
This commit is contained in:
parent
45217af29c
commit
3c3aa4516c
|
@ -67,8 +67,8 @@ assert_stmt: 'assert' test [',' test]
|
|||
|
||||
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
|
||||
async_stmt: ASYNC (funcdef | with_stmt | for_stmt)
|
||||
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
|
||||
while_stmt: 'while' test ':' suite ['else' ':' suite]
|
||||
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
|
||||
while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
|
||||
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||
try_stmt: ('try' ':' suite
|
||||
((except_clause ':' suite)+
|
||||
|
@ -91,6 +91,7 @@ testlist_safe: old_test [(',' old_test)+ [',']]
|
|||
old_test: or_test | old_lambdef
|
||||
old_lambdef: 'lambda' [varargslist] ':' old_test
|
||||
|
||||
namedexpr_test: test [':=' test]
|
||||
test: or_test ['if' or_test 'else' test] | lambdef
|
||||
or_test: and_test ('or' and_test)*
|
||||
and_test: not_test ('and' not_test)*
|
||||
|
@ -111,8 +112,8 @@ atom: ('(' [yield_expr|testlist_gexp] ')' |
|
|||
'{' [dictsetmaker] '}' |
|
||||
'`' testlist1 '`' |
|
||||
NAME | NUMBER | STRING+ | '.' '.' '.')
|
||||
listmaker: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
||||
testlist_gexp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
||||
listmaker: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
|
||||
testlist_gexp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
|
||||
lambdef: 'lambda' [varargslist] ':' test
|
||||
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
||||
subscriptlist: subscript (',' subscript)* [',']
|
||||
|
@ -137,6 +138,7 @@ arglist: argument (',' argument)* [',']
|
|||
# multiple (test comp_for) arguments are blocked; keyword unpackings
|
||||
# that precede iterable unpackings are blocked; etc.
|
||||
argument: ( test [comp_for] |
|
||||
test ':=' test |
|
||||
test '=' test |
|
||||
'**' test |
|
||||
'*' test )
|
||||
|
|
|
@ -178,6 +178,7 @@ opmap_raw = """
|
|||
// DOUBLESLASH
|
||||
//= DOUBLESLASHEQUAL
|
||||
-> RARROW
|
||||
:= COLONEQUAL
|
||||
"""
|
||||
|
||||
opmap = {}
|
||||
|
|
|
@ -65,7 +65,8 @@ RARROW = 55
|
|||
AWAIT = 56
|
||||
ASYNC = 57
|
||||
ERRORTOKEN = 58
|
||||
N_TOKENS = 59
|
||||
COLONEQUAL = 59
|
||||
N_TOKENS = 60
|
||||
NT_OFFSET = 256
|
||||
#--end constants--
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
|
|||
r"~")
|
||||
|
||||
Bracket = '[][(){}]'
|
||||
Special = group(r'\r?\n', r'[:;.,`@]')
|
||||
Special = group(r'\r?\n', r':=', r'[:;.,`@]')
|
||||
Funny = group(Operator, Bracket, Special)
|
||||
|
||||
PlainToken = group(Number, Funny, String, Name)
|
||||
|
|
|
@ -629,6 +629,21 @@ class TestLiterals(GrammarTest):
|
|||
self.validate(s)
|
||||
|
||||
|
||||
class TestNamedAssignments(GrammarTest):
|
||||
|
||||
def test_named_assignment_if(self):
|
||||
driver.parse_string("if f := x(): pass\n")
|
||||
|
||||
def test_named_assignment_while(self):
|
||||
driver.parse_string("while f := x(): pass\n")
|
||||
|
||||
def test_named_assignment_generator(self):
|
||||
driver.parse_string("any((lastNum := num) == 1 for num in [1, 2, 3])\n")
|
||||
|
||||
def test_named_assignment_listcomp(self):
|
||||
driver.parse_string("[(lastNum := num) == 1 for num in [1, 2, 3]]\n")
|
||||
|
||||
|
||||
class TestPickleableException(unittest.TestCase):
|
||||
def test_ParseError(self):
|
||||
err = ParseError('msg', 2, None, (1, 'context'))
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
lib2to3 now recognizes named assignment expressions (the walrus operator,
|
||||
``:=``)
|
Loading…
Reference in New Issue