Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3f1701a

Browse files
committed
Merge 3.5 (Issue #24619)
2 parents bdaaf49 + 8fb307c commit 3f1701a

13 files changed

Lines changed: 343 additions & 69 deletions

Doc/reference/compound_stmts.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,9 +685,7 @@ Execution of Python coroutines can be suspended and resumed at many points
685685
(see :term:`coroutine`). In the body of a coroutine, any ``await`` and
686686
``async`` identifiers become reserved keywords; :keyword:`await` expressions,
687687
:keyword:`async for` and :keyword:`async with` can only be used in
688-
coroutine bodies. However, to simplify the parser, these keywords cannot
689-
be used on the same line as a function or coroutine (:keyword:`def`
690-
statement) header.
688+
coroutine bodies.
691689

692690
Functions defined with ``async def`` syntax are always coroutine functions,
693691
even if they do not contain ``await`` or ``async`` keywords.

Lib/lib2to3/pgen2/tokenize.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ def generate_tokens(readline):
369369
# 'stashed' and 'ctx' are used for async/await parsing
370370
stashed = None
371371
ctx = [('sync', 0)]
372+
in_async = 0
372373

373374
while 1: # loop over lines in stream
374375
try:
@@ -436,6 +437,14 @@ def generate_tokens(readline):
436437
"unindent does not match any outer indentation level",
437438
("<tokenize>", lnum, pos, line))
438439
indents = indents[:-1]
440+
441+
cur_indent = indents[-1]
442+
while len(ctx) > 1 and ctx[-1][1] >= cur_indent:
443+
if ctx[-1][0] == 'async':
444+
in_async -= 1
445+
assert in_async >= 0
446+
ctx.pop()
447+
439448
yield (DEDENT, '', (lnum, pos), (lnum, pos), line)
440449

441450
else: # continued statement
@@ -499,7 +508,7 @@ def generate_tokens(readline):
499508
yield (STRING, token, spos, epos, line)
500509
elif initial in namechars: # ordinary name
501510
if token in ('async', 'await'):
502-
if ctx[-1][0] == 'async' and ctx[-1][1] < indents[-1]:
511+
if in_async:
503512
yield (ASYNC if token == 'async' else AWAIT,
504513
token, spos, epos, line)
505514
continue
@@ -515,6 +524,7 @@ def generate_tokens(readline):
515524
and stashed[1] == 'async'):
516525

517526
ctx.append(('async', indents[-1]))
527+
in_async += 1
518528

519529
yield (ASYNC, stashed[1],
520530
stashed[2], stashed[3],

Lib/test/badsyntax_async1.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
async def foo():
2-
def foo(a=await something()):
3-
pass
1+
async def foo(a=await something()):
2+
pass

Lib/test/badsyntax_async2.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
async def foo():
2-
def foo(a:await something()):
3-
pass
1+
async def foo(a:await something()):
2+
pass

Lib/test/badsyntax_async4.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
async def foo():
2-
async def foo(): await something()
2+
await

Lib/test/badsyntax_async9.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

Lib/test/test_coroutines.py

Lines changed: 218 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ def silence_coro_gc():
6767
class AsyncBadSyntaxTest(unittest.TestCase):
6868

6969
def test_badsyntax_1(self):
70-
with self.assertRaisesRegex(SyntaxError, 'invalid syntax'):
70+
with self.assertRaisesRegex(SyntaxError, "'await' outside"):
7171
import test.badsyntax_async1
7272

7373
def test_badsyntax_2(self):
74-
with self.assertRaisesRegex(SyntaxError, 'invalid syntax'):
74+
with self.assertRaisesRegex(SyntaxError, "'await' outside"):
7575
import test.badsyntax_async2
7676

7777
def test_badsyntax_3(self):
@@ -103,10 +103,6 @@ def test_badsyntax_8(self):
103103
import test.badsyntax_async8
104104

105105
def test_badsyntax_9(self):
106-
with self.assertRaisesRegex(SyntaxError, 'invalid syntax'):
107-
import test.badsyntax_async9
108-
109-
def test_badsyntax_10(self):
110106
ns = {}
111107
for comp in {'(await a for a in b)',
112108
'[await a for a in b]',
@@ -116,6 +112,221 @@ def test_badsyntax_10(self):
116112
with self.assertRaisesRegex(SyntaxError, 'await.*in comprehen'):
117113
exec('async def f():\n\t{}'.format(comp), ns, ns)
118114

115+
def test_badsyntax_10(self):
116+
# Tests for issue 24619
117+
118+
samples = [
119+
"""async def foo():
120+
def bar(): pass
121+
await = 1
122+
""",
123+
124+
"""async def foo():
125+
126+
def bar(): pass
127+
await = 1
128+
""",
129+
130+
"""async def foo():
131+
def bar(): pass
132+
if 1:
133+
await = 1
134+
""",
135+
136+
"""def foo():
137+
async def bar(): pass
138+
if 1:
139+
await a
140+
""",
141+
142+
"""def foo():
143+
async def bar(): pass
144+
await a
145+
""",
146+
147+
"""def foo():
148+
def baz(): pass
149+
async def bar(): pass
150+
await a
151+
""",
152+
153+
"""def foo():
154+
def baz(): pass
155+
# 456
156+
async def bar(): pass
157+
# 123
158+
await a
159+
""",
160+
161+
"""async def foo():
162+
def baz(): pass
163+
# 456
164+
async def bar(): pass
165+
# 123
166+
await = 2
167+
""",
168+
169+
"""def foo():
170+
171+
def baz(): pass
172+
173+
async def bar(): pass
174+
175+
await a
176+
""",
177+
178+
"""async def foo():
179+
180+
def baz(): pass
181+
182+
async def bar(): pass
183+
184+
await = 2
185+
""",
186+
187+
"""async def foo():
188+
def async(): pass
189+
""",
190+
191+
"""async def foo():
192+
def await(): pass
193+
""",
194+
195+
"""async def foo():
196+
def bar():
197+
await
198+
""",
199+
200+
"""async def foo():
201+
return lambda async: await
202+
""",
203+
204+
"""async def foo():
205+
return lambda a: await
206+
""",
207+
208+
"""async def foo(a: await b):
209+
pass
210+
""",
211+
212+
"""def baz():
213+
async def foo(a: await b):
214+
pass
215+
""",
216+
217+
"""async def foo(async):
218+
pass
219+
""",
220+
221+
"""async def foo():
222+
def bar():
223+
def baz():
224+
async = 1
225+
""",
226+
227+
"""async def foo():
228+
def bar():
229+
def baz():
230+
pass
231+
async = 1
232+
""",
233+
234+
"""def foo():
235+
async def bar():
236+
237+
async def baz():
238+
pass
239+
240+
def baz():
241+
42
242+
243+
async = 1
244+
""",
245+
246+
"""async def foo():
247+
def bar():
248+
def baz():
249+
pass\nawait foo()
250+
""",
251+
252+
"""def foo():
253+
def bar():
254+
async def baz():
255+
pass\nawait foo()
256+
""",
257+
258+
"""async def foo(await):
259+
pass
260+
""",
261+
262+
"""def foo():
263+
264+
async def bar(): pass
265+
266+
await a
267+
""",
268+
269+
"""def foo():
270+
async def bar():
271+
pass\nawait a
272+
"""]
273+
274+
ns = {}
275+
for code in samples:
276+
with self.subTest(code=code), self.assertRaises(SyntaxError):
277+
exec(code, ns, ns)
278+
279+
def test_goodsyntax_1(self):
280+
# Tests for issue 24619
281+
282+
def foo(await):
283+
async def foo(): pass
284+
async def foo():
285+
pass
286+
return await + 1
287+
self.assertEqual(foo(10), 11)
288+
289+
def foo(await):
290+
async def foo(): pass
291+
async def foo(): pass
292+
return await + 2
293+
self.assertEqual(foo(20), 22)
294+
295+
def foo(await):
296+
297+
async def foo(): pass
298+
299+
async def foo(): pass
300+
301+
return await + 2
302+
self.assertEqual(foo(20), 22)
303+
304+
def foo(await):
305+
"""spam"""
306+
async def foo(): \
307+
pass
308+
# 123
309+
async def foo(): pass
310+
# 456
311+
return await + 2
312+
self.assertEqual(foo(20), 22)
313+
314+
def foo(await):
315+
def foo(): pass
316+
def foo(): pass
317+
async def bar(): return await_
318+
await_ = await
319+
try:
320+
bar().send(None)
321+
except StopIteration as ex:
322+
return ex.args[0]
323+
self.assertEqual(foo(42), 42)
324+
325+
async def f():
326+
async def g(): pass
327+
await z
328+
self.assertTrue(inspect.iscoroutinefunction(f))
329+
119330

120331
class TokenizerRegrTest(unittest.TestCase):
121332

@@ -461,8 +672,7 @@ def test_await_8(self):
461672
class Awaitable:
462673
pass
463674

464-
async def foo():
465-
return (await Awaitable())
675+
async def foo(): return await Awaitable()
466676

467677
with self.assertRaisesRegex(
468678
TypeError, "object Awaitable can't be used in 'await' expression"):

Lib/test/test_grammar.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,10 +1051,7 @@ def async():
10511051

10521052
async def test():
10531053
def sum():
1054-
async = 1
1055-
await = 41
1056-
return async + await
1057-
1054+
pass
10581055
if 1:
10591056
await someobj()
10601057

Lib/test/test_tokenize.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,12 +786,12 @@
786786
NAME 'def' (2, 2) (2, 5)
787787
NAME 'foo' (2, 6) (2, 9)
788788
OP '(' (2, 9) (2, 10)
789-
NAME 'await' (2, 10) (2, 15)
789+
AWAIT 'await' (2, 10) (2, 15)
790790
OP ')' (2, 15) (2, 16)
791791
OP ':' (2, 16) (2, 17)
792792
NEWLINE '\\n' (2, 17) (2, 18)
793793
INDENT ' ' (3, 0) (3, 4)
794-
NAME 'await' (3, 4) (3, 9)
794+
AWAIT 'await' (3, 4) (3, 9)
795795
OP '=' (3, 10) (3, 11)
796796
NUMBER '1' (3, 12) (3, 13)
797797
NEWLINE '\\n' (3, 13) (3, 14)
@@ -829,6 +829,17 @@
829829
OP ':' (2, 18) (2, 19)
830830
NAME 'pass' (2, 20) (2, 24)
831831
DEDENT '' (3, 0) (3, 0)
832+
833+
>>> dump_tokens('''async def foo(async): await''')
834+
ENCODING 'utf-8' (0, 0) (0, 0)
835+
ASYNC 'async' (1, 0) (1, 5)
836+
NAME 'def' (1, 6) (1, 9)
837+
NAME 'foo' (1, 10) (1, 13)
838+
OP '(' (1, 13) (1, 14)
839+
ASYNC 'async' (1, 14) (1, 19)
840+
OP ')' (1, 19) (1, 20)
841+
OP ':' (1, 20) (1, 21)
842+
AWAIT 'await' (1, 22) (1, 27)
832843
"""
833844

834845
from test import support

0 commit comments

Comments
 (0)