1
$\begingroup$

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:

  1. 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
  2. Use asyncio.open_connection, seems like maybe the best solution so far
  3. 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?

$\endgroup$
0
$\begingroup$

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())
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.