qbittorrent-api icon indicating copy to clipboard operation
qbittorrent-api copied to clipboard

Async Support

Open Wamy-Dev opened this issue 5 months ago • 1 comments

What is the problem or limitation you are having?

I run a lot of qbittorrent servers and whenever one is down, it blocks my entire API trying to request to a downed server.

Describe the solution you'd like

Async API

Describe alternatives you've considered

Just using httpx in async mode using the API without using an awesome wrapper.

Additional context

No response

Wamy-Dev avatar Jan 12 '24 06:01 Wamy-Dev

I have wanted to add async support and have considered how this could be done.

Ideally, such an implementation would allow you to call client.torrents_info() or await client.torrents_info() with the same outcome.

The obvious first approach is probably just to define an qbittorrentapi.AsyncClient to create client from instead of the existing qbittorrentapi.Client. On its face, though, this would require effectively duplicating the entire sync API and redefining everything as async methods.

A less intensive approach with a similar outcome is probably some sort of decorator on each API method to dynamically allow it to be called as sync or async. This will probably still require separate Client and AsyncClient classes; I'm not sure if the decorator would otherwise have a way to know whether to return a coroutine or just run the method synchronously. This, though, does have the downside that the code wouldn't be native async code and would only provide the illusion of async by simply running the sync code in a separate thread.

With all that, I haven't decided if I'll eventually add async support. There's already a lot of cruft in this library and I'm hesitant to add more to it; if I were to design it today, it'd be a bit simpler and would incorporate a plan for sync/async from the beginning.

Finally, though, it has become much simpler to run sync code in an async context; for example:

client = qbittorrentapi.Client()

async def main():
    for t in await asyncio.to_thread(client.torrents_info, category="uploaded"):
        print(t.name)

asyncio.run(main())

This basically extracts what a decorator within this library would do to simulate async support. Given this, anyone can use this library in an async context today.

rmartin16 avatar Jan 12 '24 17:01 rmartin16