async def test_join_cleanup_external2(): """ Ensure that when the scope is done, all tasks have been cleaned properly. """ done = False scope = Scope() async def job(): nonlocal done try: await asyncio.sleep(10) finally: done = True async def runner(scope): async with scope: scope << job() task = asyncio.ensure_future(runner(scope)) # Wait a bit so that everyone is started and blocked await asyncio.sleep(0.2) # Now: scope should be joining, pending on job assert not done # Cancel the scope and await the scope scope.cancel() await scope # As soon as scope is done, we should have cleaned up job assert done await task
async def test_join_cleanup(): """ Ensure that when the scope is done, all tasks have been cleaned properly. """ done = False async def job(): nonlocal done try: await asyncio.sleep(10) finally: done = True scope = Scope() # will run 0.2 seconds scope << job() await asyncio.sleep(0.1) scope.cancel() await scope assert done
async def test_join_cancel(wait): """ """ parent = Scope() await asyncio.sleep(0.1) parent.cancel() if wait: await parent else: await asyncio.wait_for(parent, 1)
async def test_nested_cancel_wait(): """ """ parent = Scope() child = parent.fork() child << run10() await asyncio.sleep(0.1) child.cancel() parent.cancel() await asyncio.wait_for(asyncio.shield(parent), 1)
async def test_cancel_finally_cancel(): """ Cancelled internally, twice """ async def cleaner(): await asyncio.sleep(0.2) raise ValueError('boom') scope = Scope() with pytest.raises(ValueError): try: async with scope: scope << cleaner() finally: scope.cancel()
async def test_nested_cancel_join(wait): """ """ parent = Scope() child = parent.fork() child << run10() await asyncio.sleep(0.1) child.cancel() parent.cancel() if wait: await parent else: await asyncio.wait_for(parent, 1)
class Server: """Async TCP server using a scope to clean up everything""" def __init__(self): self.scope = Scope(name='TCP') self.scope.add_done_callback(self.stop) self.server = None def stop(self, _): print('scope done. closing server') if self.server: self.server.close() async def run(self): # Start TCP server self.server = await asyncio.start_server(self.client_connected, '0.0.0.0', 5000) # Join the scope forever await self.scope async def client_connected(self, reader, writer): peer = writer.get_extra_info('peername') print(peer, 'connected') async with self.scope.fork(name=peer) as ctx: # We make one context for this connection # Here it's overkill, but you could have to spawn many jobs! try: while True: try: data = await (ctx << reader.readline()) except OSError as ex: print(peer, 'socket error:', ex) break if not data: # Connection closed break txt = data.decode().strip() print(peer, 'received:', txt) if txt == 'stop': # Cancel the scope and exit print('>>> stopping server') writer.write('Closing!\n'.encode()) self.scope.cancel() break # Write reply and loop writer.write('Hello {}!\n'.format(txt).encode()) # Just for fun, print later the received message ctx << self.print_later(txt) finally: # Always say goodbye writer.write('Bye!\n'.encode()) writer.close() print(peer, 'closed') @staticmethod async def print_later(txt): await asyncio.sleep(1) print('received earlier:', txt)