Browse Source
Before this patch the context manager's __aexit__() method would not be executed if a return/break/continue statement was used to exit an async with block. async with now has the same semantics as normal with. The fix here applies purely to the compiler, and does not modify the runtime at all. It might (eventually) be better to define new bytecode(s) to handle async with (and maybe other async constructs) in a cleaner, more efficient way. One minor drawback with addressing this issue purely in the compiler is that it wasn't possible to get 100% CPython semantics. The thing that is different here to CPython is that the __aexit__ method is not looked up in the context manager until it is needed, which is after the body of the async with statement has executed. So if a context manager doesn't have __aexit__ then CPython raises an exception before the async with is executed, whereas uPy will raise it after it is executed. Note that __aenter__ is looked up at the beginning in uPy because it needs to be called straightaway, so if the context manager isn't a context manager then it'll still raise an exception at the same location as CPython. The only difference is if the context manager has the __aenter__ method but not the __aexit__ method, then in that case uPy has different behaviour. But this is a very minor, and acceptable, difference.pull/3881/merge
Damien George
6 years ago
6 changed files with 208 additions and 38 deletions
@ -0,0 +1,59 @@ |
|||
# test async with, escaped by a break |
|||
|
|||
class AContext: |
|||
async def __aenter__(self): |
|||
print('enter') |
|||
return 1 |
|||
async def __aexit__(self, exc_type, exc, tb): |
|||
print('exit', exc_type, exc) |
|||
|
|||
async def f1(): |
|||
while 1: |
|||
async with AContext(): |
|||
print('body') |
|||
break |
|||
print('no 1') |
|||
print('no 2') |
|||
|
|||
o = f1() |
|||
try: |
|||
print(o.send(None)) |
|||
except StopIteration: |
|||
print('finished') |
|||
|
|||
async def f2(): |
|||
while 1: |
|||
try: |
|||
async with AContext(): |
|||
print('body') |
|||
break |
|||
print('no 1') |
|||
finally: |
|||
print('finally') |
|||
print('no 2') |
|||
|
|||
o = f2() |
|||
try: |
|||
print(o.send(None)) |
|||
except StopIteration: |
|||
print('finished') |
|||
|
|||
async def f3(): |
|||
while 1: |
|||
try: |
|||
try: |
|||
async with AContext(): |
|||
print('body') |
|||
break |
|||
print('no 1') |
|||
finally: |
|||
print('finally inner') |
|||
finally: |
|||
print('finally outer') |
|||
print('no 2') |
|||
|
|||
o = f3() |
|||
try: |
|||
print(o.send(None)) |
|||
except StopIteration: |
|||
print('finished') |
@ -0,0 +1,15 @@ |
|||
enter |
|||
body |
|||
exit None None |
|||
finished |
|||
enter |
|||
body |
|||
exit None None |
|||
finally |
|||
finished |
|||
enter |
|||
body |
|||
exit None None |
|||
finally inner |
|||
finally outer |
|||
finished |
@ -0,0 +1,50 @@ |
|||
# test async with, escaped by a return |
|||
|
|||
class AContext: |
|||
async def __aenter__(self): |
|||
print('enter') |
|||
return 1 |
|||
async def __aexit__(self, exc_type, exc, tb): |
|||
print('exit', exc_type, exc) |
|||
|
|||
async def f1(): |
|||
async with AContext(): |
|||
print('body') |
|||
return |
|||
|
|||
o = f1() |
|||
try: |
|||
o.send(None) |
|||
except StopIteration: |
|||
print('finished') |
|||
|
|||
async def f2(): |
|||
try: |
|||
async with AContext(): |
|||
print('body') |
|||
return |
|||
finally: |
|||
print('finally') |
|||
|
|||
o = f2() |
|||
try: |
|||
o.send(None) |
|||
except StopIteration: |
|||
print('finished') |
|||
|
|||
async def f3(): |
|||
try: |
|||
try: |
|||
async with AContext(): |
|||
print('body') |
|||
return |
|||
finally: |
|||
print('finally inner') |
|||
finally: |
|||
print('finally outer') |
|||
|
|||
o = f3() |
|||
try: |
|||
o.send(None) |
|||
except StopIteration: |
|||
print('finished') |
@ -0,0 +1,15 @@ |
|||
enter |
|||
body |
|||
exit None None |
|||
finished |
|||
enter |
|||
body |
|||
exit None None |
|||
finally |
|||
finished |
|||
enter |
|||
body |
|||
exit None None |
|||
finally inner |
|||
finally outer |
|||
finished |
Loading…
Reference in new issue