# Spawn 2 slightly different instances of the shell program from # previous exercise, then use ‹gather› to run 3 tasks in parallel: # # • two that print the output from each of the processes # • one that alternates feeding data into both of the subprocesses # # First shell program reads its input and outputs ‹p1: [input # value]›. Second shell program reads its input and outputs ‹p2: # [input value]›. Process 3 sends characters ‹a› through ‹h› to the # two printing processes; it first sends the character to process 1, # then waits 0.5 seconds, then it sends the same character to # process 2 and waits 0.2 seconds. The outputs of the two main # processes are printed to ‹stdout›, so that you can follow what is # going on, and added to the global ‹data› list, along with a # timestamp (see ‹p1›) – as a tuple. # # Don't forget to clean up at the end. import asyncio from asyncio.subprocess import PIPE from typing import Tuple data : list[ tuple[ str, float ] ] = [] async def multi() -> None: pass def test_main() -> None: asyncio.run( multi() ) error = 0.0 def check( idx: int, p: str, char: str, t: float ) -> None: nonlocal error assert len( data ) > idx p_c, tt = data[ idx ] assert tt >= t and abs( tt - t ) - error < 0.05 error = abs( tt - t ) p_, c = p_c.split( ': ' ) assert p_ == p assert c == char check( 0, 'p1', 'a', 0.0 ) check( 1, 'p2', 'a', 0.5 ) check( 2, 'p1', 'b', 0.7 ) check( 3, 'p2', 'b', 1.2 ) check( 4, 'p1', 'c', 1.4 ) check( 5, 'p2', 'c', 1.9 ) check( 6, 'p1', 'd', 2.1 ) check( 7, 'p2', 'd', 2.6 ) check( 8, 'p1', 'e', 2.8 ) check( 9, 'p2', 'e', 3.3 ) check( 10, 'p1', 'f', 3.5 ) check( 11, 'p2', 'f', 4.0 ) check( 12, 'p1', 'g', 4.2 ) check( 13, 'p2', 'g', 4.7 ) check( 14, 'p1', 'h', 4.9 ) check( 15, 'p2', 'h', 5.4 ) if __name__ == "__main__": test_main()