async def test_fork_dead(): """ Try to fork a dead scope. """ scope = Scope() async with scope: pass with pytest.raises(AssertionError): _ = scope.fork()
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_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)