Skip to main content

Windows version of uvloop

Project description

Code style: black

Winloop

An Alternative library for uvloop compatibility with windows because let's face it. Window's python asyncio standard libaray is garabage escpecially when Windows Defender decides to eat 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 when libuv is available for windows but windows was never compatible with uvloop.

Because nobody was willing to step in after so many years of waiting, I went ahead and downloaded the source code for uvloop and started modifying the source code to be windows compatible by carefully removing and changing parts that were not made for windows. Many hours of research went into making this library exist.

The differences with uvloop is that forking has been fully disabled and some smaller api calls had to be changed, error handling has been carefully modified and subprocesses instead release the gil instead of forking out...

There is a performance increase of about 5 times vs using the WindowsSelectorEventLoopPolicy and WindowsProactorEventLoopPolicy which have been known to trigger ssl problems in python 3.9. Winloop is a very good replacement for solving those ssl problem as well. This library also has comparable performance to it's brother uvloop.

How to install Winloop on your Windows Operating System

pip install winloop

You can also clone the reposity and build the extension yourself by running the command below if you wish to use or build this library locally, Note that you will need Cython and The Visual C++ extensions to compile this library on your own.

python setup.py build_ext --inplace

Issues Reporting

If you find any bugs with this library be sure to open up an issue to our github repo. Me and other contributors will be happy try to help you figure out and diagnose your problems.

Making pull requests

We encourage anyone to make pull-requests to winloop from spelling mistakes to vulnerability patches. Every little bit helps keep this library maintained and alive. Make sure that you are able to compile the library with the steps shown above. We plan to implement a nightly workflow to verify one's pull request in the future.

try:
    import aiohttp
    import aiohttp.web
except ImportError:
    skip_tests = True
else:
    skip_tests = False

import asyncio
import unittest
import weakref
import winloop
import sys

class TestAioHTTP(unittest.TestCase):
    def __init__(self, methodName: str = "test_aiohttp_basic_1") -> None:
        super().__init__(methodName)


    def setUp(self):
        self.loop = asyncio.get_event_loop()

    def test_aiohttp_basic_1(self):
        PAYLOAD = '<h1>It Works!</h1>' * 10000

        async def on_request(request):
            return aiohttp.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]

        async def test():
            # Make sure we're using the correct event loop.
            self.assertIs(asyncio.get_event_loop(), self.loop)

            for addr in (('localhost', port),
                         ('127.0.0.1', port)):
                async with aiohttp.ClientSession() as client:
                    async with client.get('http://{}:{}'.format(*addr)) as r:
                        self.assertEqual(r.status, 200)
                        result = await r.text()
                        self.assertEqual(result, PAYLOAD)

        self.loop.run_until_complete(test())
        self.loop.run_until_complete(runner.cleanup())

    def test_aiohttp_graceful_shutdown(self):
        async def websocket_handler(request):
            ws = aiohttp.web.WebSocketResponse()
            await ws.prepare(request)
            request.app['websockets'].add(ws)
            try:
                async for msg in ws:
                    await ws.send_str(msg.data)
            finally:
                request.app['websockets'].discard(ws)
            return ws

        async def on_shutdown(app):
            for ws in set(app['websockets']):
                await ws.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]

        async def client():
            async with aiohttp.ClientSession() as client:
                async with client.ws_connect(
                        'http://127.0.0.1:{}'.format(port)) as ws:
                    await ws.send_str("hello")
                    async for msg in ws:
                        assert msg.data == "hello"

        client_task = asyncio.ensure_future(client())

        async def stop():
            await asyncio.sleep(0.1)
            try:
                await asyncio.wait_for(runner.cleanup(), timeout=0.1)
            except Exception as e:
                print(e)
            finally:
                try:
                    client_task.cancel()
                    await client_task
                except asyncio.CancelledError:
                    pass

        self.loop.run_until_complete(stop())



if __name__ == "__main__":
    # print("tesing without winloop")
    # asyncio.DefaultEventLoopPolicy = asyncio.WindowsSelectorEventLoopPolicy
    # asyncio.DefaultEventLoopPolicy = asyncio.WindowsProactorEventLoopPolicy
    unittest.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 follows

Benchmarks

TCP Connections


Asyncio Event Loop Policy Time (in Seconds)
WinLoopPolicy 0.493s
WindowsProactorEventLoopPolicy 2.510s
WindowsSelectorEventLoopPolicy 2.723s

That'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 Fastapi

This was a cool little script I put together Just to make fastapi that much faster to handle

# TODO this code example is deprecated
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import winloop
import uvicorn
import asyncio
import datetime

app = FastAPI()

@app.on_event("startup")
def make_assertion():
    # Check to make sure that we bypassed the original eventloop Policy....
    assert isinstance(asyncio.get_event_loop_policy(), winloop.WinLoopPolicy)


@app.get("/test")
async def test_get_request():
    return HTMLResponse("<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")
def test_dynamic_response():
    return str(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 available

# Here's A small Example of using winloop when uvloop is not available to us
import sys
import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession("https://httpbin.org") as client:
        async with client.get("/ip") as resp:
            print(await resp.json())

if __name__ == "__main__":
    if sys.platform in ('win32', 'cygwin', 'cli'):
        from winloop import run
    else:
        # if we're on apple or linux do this instead
        from uvloop import run
    run(main())

TODO-List

  • Update Fastapi Example to a more recent version of fastapi

  • Contact uvicorn about this issue I have a funny feeling something is going on here...

  • Nightly Builds And Test Suite Workflows for anyone wanting to use newer unreleased versions.

  • Drop All DEF Macros, I'm currently seeking help on replacements for macros where all the variables are known about at compile-time

  • Adding in the necessary hooks for pyinstaller to compile this fast library to executable code even though hooks have been known to inflate the size of the .exe files. This is because calling hidden-imports for all the __init__.py modules might annoy some developers. (Luckily I'm aware of this issue because I've been doing this myself...)

  • write a workflow for compiling libuv on different versions of windows when distributing out pypi wheels.

  • write a workflow for nightly builds if necessary for verification of pull requests.

  • Sphinx Styled Documentation (Maybe I'm thinking about it...)

  • Update benchmarks (They are old) can't believe I maintained this project for over a year now...

Videos

Contributing

I put my heart and soul into this library ever since it began and any help is apperciated and means a lot to me.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

winloop-0.1.6.tar.gz (1.8 MB view details)

Uploaded Source

Built Distributions

winloop-0.1.6-cp312-cp312-win_amd64.whl (582.0 kB view details)

Uploaded CPython 3.12 Windows x86-64

winloop-0.1.6-cp311-cp311-win_amd64.whl (583.7 kB view details)

Uploaded CPython 3.11 Windows x86-64

winloop-0.1.6-cp310-cp310-win_amd64.whl (599.9 kB view details)

Uploaded CPython 3.10 Windows x86-64

winloop-0.1.6-cp39-cp39-win_amd64.whl (616.7 kB view details)

Uploaded CPython 3.9 Windows x86-64

winloop-0.1.6-cp38-cp38-win_amd64.whl (615.8 kB view details)

Uploaded CPython 3.8 Windows x86-64

File details

Details for the file winloop-0.1.6.tar.gz.

File metadata

  • Download URL: winloop-0.1.6.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6.tar.gz
Algorithm Hash digest
SHA256 bd8cda3a4281b5bb4368f8ed94e833421738ee8dba1d208ce8edc1d476eb7b9c
MD5 0e27489e68b90b85561bf3b0001448e0
BLAKE2b-256 6f998d8a70a784acf07c7f52dd1c5cd3015c02ec250bbb69dcf408ada1137384

See more details on using hashes here.

File details

Details for the file winloop-0.1.6-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: winloop-0.1.6-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 582.0 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 9aa0118354f07b2f36953c88f3767fa9832e4de9be2ee76419b8de72a8315cce
MD5 6a8495e9cec4e8f070d12c34d1504e75
BLAKE2b-256 d0d54c7c94d6ec58ef166ac6a67ad1ab25a6919f084676907f51dcebe992b789

See more details on using hashes here.

File details

Details for the file winloop-0.1.6-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: winloop-0.1.6-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 583.7 kB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 21c672482bb3a55c6c73030242e7555315ad12cb7382a4879f8e08d99bc8d976
MD5 6b6fc8add5938a484479b4016cc04011
BLAKE2b-256 3c2a6100fc5623016828558a55d6beb34b1df68553d6824d1394caf0acfcf0cd

See more details on using hashes here.

File details

Details for the file winloop-0.1.6-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: winloop-0.1.6-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 599.9 kB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 579b74173444d2f81a7db2f3dc40c282b68d926f7ef3e60fd70dff0db86502d9
MD5 e800d4a3dae123b4893e3ffc2ef312c3
BLAKE2b-256 bd0a6176f73a99bda24f1df4324f9c89a343dee23dcea0552ba0d83eb464e0c3

See more details on using hashes here.

File details

Details for the file winloop-0.1.6-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: winloop-0.1.6-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 616.7 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 180b55fe6a7fddb2a67bdc9b9d0a1cad22d144e5508abc9d8e6a52083ebee845
MD5 6d4b227f85681fbea01f978c8349654a
BLAKE2b-256 3b4a0a01385872988b535d29bc4e95896fa744b9abd7150bdfcee1153b68f2a0

See more details on using hashes here.

File details

Details for the file winloop-0.1.6-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: winloop-0.1.6-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 615.8 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.11.9

File hashes

Hashes for winloop-0.1.6-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 ee5114a971910b6d7ad4fcaf855838b53995c3f6213cd4a78a35644cd2309c1f
MD5 b120c8cfb1d4fe4b59178d7b077481ed
BLAKE2b-256 34c16827172dd3b1790665d38dbcc634a2156914871ec463276d6f63c5e13049

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page