Package: winloop
WinloopAn Alternative library for uvloop compatability with windows because let's face it. Window's python asyncio is garabage escpecially when Windows Defender eats up half your ram.
I never really liked the fact that I couldn't make anything run faster escpecially when you have fiber internet connections in place and you've done all the optimizations you could possibly think of. It always felt disappointing whenlibuvis avalible on windows but doesn't havecompatability with uvloop at all.Because nobody was willing to step in after so many years of people waiting , I went ahead and downloaded the uvloop source code and modified the source code to be windows compatable by carefully removing and changing parts that were not made for windows, many hours of research went into making this library exist.The differences withuvloopis that forking has been fully disabled and some smaller api calls had to be changed. Subprocesses instead release the gil instead of forking out although I might change that in the future if handling asynchronous subprocesses becomes a problem to handle...There is a perfromance increase of about 5 times vs using theWindowsSelectorEventLoopPolicyandWindowsProactorEventLoopPolicywhich have been known to trigger ssl problems in python 3.9. Winloop is a very good replacement for those ssl problem as well.How to install Winloop on your Operating Systempip install winloopyou can also clone the reposity and build the extension yourself by running the command below if you wish to use or build this library locallypython setup.py build_ext --inplaceIf you find any sneaky bugs with this library be sure to open up an issue to our github repo. Me and other contributors will be happy to help you figure out and diagnose your problems.try:importaiohttpimportaiohttp.webexceptImportError:skip_tests=Trueelse:skip_tests=FalseimportasyncioimportunittestimportweakrefimportwinloopimportsysclassTestAioHTTP(unittest.TestCase):def__init__(self,methodName:str="test_aiohttp_basic_1")->None:super().__init__(methodName)defsetUp(self):self.loop=asyncio.get_event_loop()deftest_aiohttp_basic_1(self):PAYLOAD='<h1>It Works!</h1>'*10000asyncdefon_request(request):returnaiohttp.web.Response(text=PAYLOAD)asyncio.set_event_loop(self.loop)app=aiohttp.web.Application()app.router.add_get('/',on_request)runner=aiohttp.web.AppRunner(app)self.loop.run_until_complete(runner.setup())site=aiohttp.web.TCPSite(runner,'0.0.0.0','10000')self.loop.run_until_complete(site.start())port=site._server.sockets[0].getsockname()[1]asyncdeftest():# Make sure we're using the correct event loop.self.assertIs(asyncio.get_event_loop(),self.loop)foraddrin(('localhost',port),('127.0.0.1',port)):asyncwithaiohttp.ClientSession()asclient:asyncwithclient.get('http://{}:{}'.format(*addr))asr:self.assertEqual(r.status,200)result=awaitr.text()self.assertEqual(result,PAYLOAD)self.loop.run_until_complete(test())self.loop.run_until_complete(runner.cleanup())deftest_aiohttp_graceful_shutdown(self):asyncdefwebsocket_handler(request):ws=aiohttp.web.WebSocketResponse()awaitws.prepare(request)request.app['websockets'].add(ws)try:asyncformsginws:awaitws.send_str(msg.data)finally:request.app['websockets'].discard(ws)returnwsasyncdefon_shutdown(app):forwsinset(app['websockets']):awaitws.close(code=aiohttp.WSCloseCode.GOING_AWAY,message='Server shutdown')asyncio.set_event_loop(self.loop)app=aiohttp.web.Application()app.router.add_get('/',websocket_handler)app.on_shutdown.append(on_shutdown)app['websockets']=weakref.WeakSet()runner=aiohttp.web.AppRunner(app)self.loop.run_until_complete(runner.setup())site=aiohttp.web.TCPSite(runner,'0.0.0.0','10000')self.loop.run_until_complete(site.start())port=site._server.sockets[0].getsockname()[1]asyncdefclient():asyncwithaiohttp.ClientSession()asclient:asyncwithclient.ws_connect('http://127.0.0.1:{}'.format(port))asws:awaitws.send_str("hello")asyncformsginws:assertmsg.data=="hello"client_task=asyncio.ensure_future(client())asyncdefstop():awaitasyncio.sleep(0.1)try:awaitasyncio.wait_for(runner.cleanup(),timeout=0.1)exceptExceptionase:print(e)finally:try:client_task.cancel()awaitclient_taskexceptasyncio.CancelledError:passself.loop.run_until_complete(stop())if__name__=="__main__":# print("tesing without winloop")# asyncio.DefaultEventLoopPolicy = asyncio.WindowsSelectorEventLoopPolicy# asyncio.DefaultEventLoopPolicy = asyncio.WindowsProactorEventLoopPolicyunittest.main()# Looks like winloop might be 3x faster than the Proctor Event Loop , THAT's A HUGE IMPROVEMENT!print("testing again but with winloop enabled")winloop.install()unittest.main()The benchmarks for the code above are as followsBenchmarksTCP ConnectionsAsyncio Event Loop PolicyTime (in Seconds)WinLoopPolicy0.493sWindowsProactorEventLoopPolicy2.510sWindowsSelectorEventLoopPolicy2.723sThat's a massive increase and jump from just TCP alone I'll be posting more benchmarks soon as
I modify more of the current test suites made by uvloop...How to Use Winloop with FastapiThis was a cool little script I put together Just to make fastapi that much faster to handlefromfastapiimportFastAPIfromfastapi.responsesimportHTMLResponseimportwinloopimportuvicornimportasyncioimportdatetimeapp=FastAPI()@app.on_event("startup")defmake_assertion():# Check to make sure that we bypassed the original eventloop Policy....assertisinstance(asyncio.get_event_loop_policy(),winloop.WinLoopPolicy)@app.get("/test")asyncdeftest_get_request():returnHTMLResponse("<html><body><h1>FAST API WORKS WITH WINLOOP!</h1></body></html>")# starllete will use asyncio.to_thread() so that this can remain asynchronous@app.get("/date")deftest_dynamic_response():returnstr(datetime.datetime.now())# Although tricky to pass and is not normal, it does in fact work...if__name__=="__main__":winloop.install()# Winloop's eventlooppolicy will be passed to uvicorn after this point...loop=asyncio.get_event_loop()config=uvicorn.Config(app=app,port=10000,loop=loop)server=uvicorn.Server(config)asyncio.run(server.serve())How To Use Winloop When Uvloop is not avalible# Here's A small Example of using winloop when uvloop is not avalible to usimportsysimportaiohttpimportasyncioasyncdefmain():asyncwithaiohttp.ClientSession("https://httpbin.org")asclient:asyncwithclient.get("/ip")asresp:print(awaitresp.json())if__name__=="__main__":ifsys.platformin('win32','cygwin','cli'):fromwinloopimportinstallelse:# if we're on apple or linux do this insteadfromuvloopimportinstallinstall()asyncio.run(main())Possible Upcomming Features/Optimizations to Winloop / This is also our TODO listI have been looking deeply into some of the proposed Pulls and changes toUvloopand I will be tuning in and listening to what's going on uvloop's end to see what we might have to change . This is a list of features I would like to implement from likely to least likely to be done as well as solved. If any of these feature have been added you will simply know by the fact that It won't be on this list anymore...Drop AllDEFMacros, I'm currently seeking help on replacements for macros where all the variables are known about at compile-timeMake a sucessful Github Workflow for uploading wheels to pypi any help and guidence would be appreciateddelete loop.c on install once the code has been compiled to a.pydfile sinceloop.cbecomes 8 Microbytes (8MB) of waste at that point (yeah it's very big). Users don't need a file this big escpecailly for those who are diskspace sensetive like myself.Adding in the nessesary hooks for pyinstaller to compile this fast library to executable code even though hooks have been known to inflate the size of the.exefiles. This is because calling hidden-imports for all the__init__.pymodules might annoy some developers. (Luckily I'm aware of this issue because I've been doing this myself...)Drop custom Socketpair function inside ofsocketpair.hin replacement for libvs's function until we can better understand how its need to be implemented so that we can make it go a bit deeper to be a bit more quick at creating sockets , the assert checks might be dropped as long as there's no problems with it. I'll be sure to reupload the droppedsocketpair.hcode of mine into a gist if anyone still wanted to go ahead and use it elsewhere although it may have many bugs of it's own which was my original reason for not using it when I discovered that libuv had it's own socketpair functions for windows...Easier Error Diagnosis to code even though I've found that most errors are not arising from winloop's own code and it is being triggered from elsewhere (This is a good thing but makes it harder to tell where other devs mess up) in fact, winloop actually passes more tests than python's asyncio event loop policies do. This might just be both a blessing and a curse for us...Maybe Drop and then re-join the gil after the subprocess is first spawned in instead of joining before being killed off by the end user (This will need to be accompanied by some heavy unittesting no doubt)Optimzing TCP Connections as well as sending data instreams.pyxwith the uv bites are finally dropped in the try write portions of our library. Currently uv bites is just there as a protection measure by me, this will be dropped in the future since the try_write block has some subprocess checks as well as long as it doesn't have subprocess behaviors I'm alright with upgradingstreams.pyxas long as it doesn't break. I did leave my plan uncommented for right now but the other half belonging to subprocesses looks rather steep/deep.drop the.libfile for compiling libuv in replacement for libuv's C files directly once the current compiling problems and errors have been solved.VideosBy me:https://www.youtube.com/watch?v=tz9RYJ6aBZ8(I might make a tutorial on how to use and install winloop it for those who have reading problems)My Presentation and Virtual Confrence:https://www.youtube.com/watch?v=Cbb6trkKWXY