Best way to send network requests from timer modal I'm working on an addon that has a polling loop (using a timer modal) that needs to send a network request on every iteration (once a minute):

class PollingLoopOperator(bpy.types.Operator):
    """Polling loop"""
    bl_idname = "wm.modal_polling_loop"
    bl_label = "Polling Loop"

    _timer = None

    def modal(self, context, event):
        if event.type == 'TIMER':
            # Run network polling request

        return {'PASS_THROUGH'}

    def execute(self, context):
        wm = context.window_manager
        # Run every minute
        self._timer = wm.event_timer_add(60, context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}


At first I was thinking of just using the requests library, but then I realized that wouldn't work because it's blocking, and sending a blocking network request every minute would be terrible from a UX perspective. I can't use another thread because Blender is not thread safe. So far I've thought of the fallowing possible solutions, but they don't seem that great:


Use multiprocessing to run the request in another process, I could still use the requests library, but it would have the added overhead of an additional process
Use asyncio.open_connection, seems like maybe the best solution so far
Keep a non blocking socket open and push data from the server, the polling loop just checks to see if the socket has any data. This is a pretty low level option and would require manual ssl handling and building a custom protocol. Plus, it would be harder to build the backend for. But it would have a few major benefits:


The polling loop could run much more frequently then 1 minute because all it would do is check to see if there is any data in the socket
Less overhead because there is only a single socket connection opened



Which of these would be the best solution? Or, is there an even better solution I haven't thought of?
I ended up doing the same thing as the Blender Cloud Addon and run a requests call in a threading executor:

import asyncio
import requests
import functools

async def do_request():
    partial = functools.partial(requests.get, 'http://example.com')
    loop = asyncio.get_event_loop()
    response = await loop.run_in_executor(None, partial)
    print(response)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(do_request())