Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3c3aa45

Browse files
authoredApr 2, 2020
lib2to3: Support named assignment expressions (pythonGH-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.
1 parent 45217af commit 3c3aa45

File tree

6 files changed

+27
-6
lines changed

6 files changed

+27
-6
lines changed
 

‎Lib/lib2to3/Grammar.txt

+6-4
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ assert_stmt: 'assert' test [',' test]
6767

6868
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
6969
async_stmt: ASYNC (funcdef | with_stmt | for_stmt)
70-
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
71-
while_stmt: 'while' test ':' suite ['else' ':' suite]
70+
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
71+
while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
7272
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
7373
try_stmt: ('try' ':' suite
7474
((except_clause ':' suite)+
@@ -91,6 +91,7 @@ testlist_safe: old_test [(',' old_test)+ [',']]
9191
old_test: or_test | old_lambdef
9292
old_lambdef: 'lambda' [varargslist] ':' old_test
9393

94+
namedexpr_test: test [':=' test]
9495
test: or_test ['if' or_test 'else' test] | lambdef
9596
or_test: and_test ('or' and_test)*
9697
and_test: not_test ('and' not_test)*
@@ -111,8 +112,8 @@ atom: ('(' [yield_expr|testlist_gexp] ')' |
111112
'{' [dictsetmaker] '}' |
112113
'`' testlist1 '`' |
113114
NAME | NUMBER | STRING+ | '.' '.' '.')
114-
listmaker: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
115-
testlist_gexp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
115+
listmaker: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
116+
testlist_gexp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
116117
lambdef: 'lambda' [varargslist] ':' test
117118
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
118119
subscriptlist: subscript (',' subscript)* [',']
@@ -137,6 +138,7 @@ arglist: argument (',' argument)* [',']
137138
# multiple (test comp_for) arguments are blocked; keyword unpackings
138139
# that precede iterable unpackings are blocked; etc.
139140
argument: ( test [comp_for] |
141+
test ':=' test |
140142
test '=' test |
141143
'**' test |
142144
'*' test )

‎Lib/lib2to3/pgen2/grammar.py

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def report(self):
178178
// DOUBLESLASH
179179
//= DOUBLESLASHEQUAL
180180
-> RARROW
181+
:= COLONEQUAL
181182
"""
182183

183184
opmap = {}

‎Lib/lib2to3/pgen2/token.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@
6565
AWAIT = 56
6666
ASYNC = 57
6767
ERRORTOKEN = 58
68-
N_TOKENS = 59
68+
COLONEQUAL = 59
69+
N_TOKENS = 60
6970
NT_OFFSET = 256
7071
#--end constants--
7172

‎Lib/lib2to3/pgen2/tokenize.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def _combinations(*l):
9393
r"~")
9494

9595
Bracket = '[][(){}]'
96-
Special = group(r'\r?\n', r'[:;.,`@]')
96+
Special = group(r'\r?\n', r':=', r'[:;.,`@]')
9797
Funny = group(Operator, Bracket, Special)
9898

9999
PlainToken = group(Number, Funny, String, Name)

‎Lib/lib2to3/tests/test_parser.py

+15
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,21 @@ def test_multiline_str_literals(self):
629629
self.validate(s)
630630

631631

632+
class TestNamedAssignments(GrammarTest):
633+
634+
def test_named_assignment_if(self):
635+
driver.parse_string("if f := x(): pass\n")
636+
637+
def test_named_assignment_while(self):
638+
driver.parse_string("while f := x(): pass\n")
639+
640+
def test_named_assignment_generator(self):
641+
driver.parse_string("any((lastNum := num) == 1 for num in [1, 2, 3])\n")
642+
643+
def test_named_assignment_listcomp(self):
644+
driver.parse_string("[(lastNum := num) == 1 for num in [1, 2, 3]]\n")
645+
646+
632647
class TestPickleableException(unittest.TestCase):
633648
def test_ParseError(self):
634649
err = ParseError('msg', 2, None, (1, 'context'))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lib2to3 now recognizes named assignment expressions (the walrus operator,
2+
``:=``)

0 commit comments

Comments
 (0)
Please sign in to comment.