Package: pygoic
pygoicpygoicis a Go-like asynchronous concurrency library for python based on asyncio.importasynciofrompygoicimportgo,do,Chan,selectch=Chan()asyncdeffoo1():awaitasyncio.sleep(0.02)x,ok=awaitch.recv()print(x,ok)# output: 'c', Trueasyncdeffoo2():awaitasyncio.sleep(0.01)awaitch.send('a')asyncdeffoo():go(foo1())go(foo2())id,x,ok=awaitselect(ch.case_recv(),ch.case_send('b'))print(id,x,ok)# output: 0, 'a', Trueid,x,ok=awaitselect(ch.case_recv(),ch.case_send('c'))print(id,x,ok)# output: 1, 'c', Truedo(foo())# block until foo donepygoic allows you to levarage coroutine in python extremely easily. There's no need to manually deal with trival and error-prone details, especially about event loop, which is designed to be so anti-human. This library will try its best to help you forgot those unpleasant things.Follow a few simple principles, and you will get a Go-like concurrent programming experience.PrinciplesDon't call anyblockingoperation inside coroutines. If not avoidable, please usedelegate.Functiondocan only be called out of coroutines, whilegois for anywhere.Installation$pipinstallpygoicQuick Tutorialgo and dogoaccepts a coroutine object (call of async funcion) as parameter, and start executing it concurrently (not accurate). You can expectgoto behave the same as in Golang. Except it will return anawaitable, which can be consumed byawaitordoif needed.doalso accepts a coroutine object as parameter. The difference is that it willblockuntil coroutine object is finished, and will return the result.importasynciofrompygoicimportgo,doasyncdeffoo1(t):awaitasyncio.sleep(0.01)print(f'foo1:{t}')return'hello'asyncdeffoo2():x=go(foo1(2))print('foo2')print(awaitx)return'world'go(foo1(1))print(do(foo2()))### Output #### foo2# foo1: 1# foo1: 2# hello# worlddelegatedelegateaccepts a function and the args to call this function. It will execute this funcion in a thread pool, and return anawaitable. Looks like it is an asynchronous operation.importtimefrompygoicimportgo,do,delegateasyncdeffoo1():awaitdelegate(time.sleep,0.02)print('foo1')asyncdeffoo2():awaitdelegate(time.sleep,0.01)print('foo2')x=go(foo1())go(foo2())do(x)### Output #### foo2# foo1Chan and selectBehavior ofChanis similar to that in Golang.selectacceptsChans as parameters, and returns when any of theChanoperations is unblocked.frompygoicimportChan,select,go,doch1=Chan()ch2=Chan()asyncdeffoo1():awaitch1.send('a')awaitch2.send('b')awaitch2.recv()asyncdeffoo2():id,item,ok=awaitselect(ch1,ch2)ifid==0:print(f'foo2: 0, read ch1')elifid==1:print(f'foo2: 0, read ch2')id,item,ok=awaitselect(ch2.case_recv(),ch2.case_send('c'))ifid==0:print(f'foo2: 1, read ch2')elifid==1:print(f'foo2: 1, write ch2')id,item,ok=awaitselect(ch2.case_recv(),ch2.case_send('d'))ifid==0:print(f'foo2: 2, read ch2')elifid==1:print(f'foo2: 2, write ch2')go(foo1())do(foo2())### Output #### foo2: 0, read ch1# foo2: 1, read ch2# foo2: 2, write ch2ContextYou can expectContextto behave the same as in Golang.importasynciofrompygoicimportselect,go,dofrompygoicimportBackground,WithCancel,WithTimeout,WithValuectx1=WithValue(Background(),'k','v')ctx2,_=WithTimeout(ctx1,0.01)ctx3,cancel=WithCancel(ctx2)asyncdeffoo1():awaitasyncio.sleep(0.02)cancel()asyncdeffoo2():print(ctx3.value('k'))go(foo1())id,item,ok=awaitselect(ctx2.done(),ctx3.done())ifid==0:print('foo2: ctx2')elifid==1:print('foo2: ctx3')go(foo1())go(foo2())do(ctx3.done().recv())### Output #### v# foo2: ctx2After, AfterFunc and TimerBehave the same as in Golang.frompygoicimportgo,do,select,ChanfrompygoicimportAfter,AfterFuncch=Chan()asyncdeffoo1():ch.close()asyncdeffoo2():AfterFunc(0.015,foo1)id,item,ok=awaitselect(After(0.01),ch)ifid==0:print('foo2: after')elifid==1:print('foo2: ch')id,item,ok=awaitselect(After(0.01),ch)ifid==0:print('foo2: after')elifid==1:print('foo2: ch')do(foo2())### Output #### foo2: after# foo2: chWaitGroupBehave the same as in Golang.importasynciofrompygoicimportgo,do,WaitGroupwg=WaitGroup(2)asyncdeffoo1():wg.done()awaitasyncio.sleep(0.01)print('foo1: 0')wg.done()awaitasyncio.sleep(0.01)print('foo1: 1')asyncdeffoo2():print('foo2: 0')awaitwg.wait()print('foo2: 1')go(foo2())do(foo1())### Output #### foo2: 0# foo1: 0# foo2: 1# foo1: 1