I've also tried to call
asyncio.run(async_task())
directly, and following error was raised:
File "D:\Program Files\Side Effects Software\Houdini 20.0.688\python310\lib\asyncio\runners.py", line 33, in run raise RuntimeError( RuntimeError: asyncio.run() cannot be called from a running event loop
Is it still possible to do async things in python node for Houdini 20? And what's the proper way?
Hello
I would be interested in that too! I have issues running the same asyncio code (that worked in Houdini 19.5) in Houdini 20.
In my case i was running the asyncio loop inside of a thread.
But in H20 this does not seem to be allowed anyomore?
Traceback (most recent call last):
File "C:\PROGRA~1\SIDEEF~1\HOUDIN~1.724\python310\lib\threading.py", line 1016, in _bootstrap_inner
15:00:26 DEBUG:hatchet.base: Data: Start timers
self.run()
File "my_script.py", line 170, in run
self._loop.create_task(self._async_start_heartbeat())
File "C:\PROGRA~1/SIDEEF~1/HOUDIN~1.724/houdini/python3.10libs\haio.py", line 2165, in create_task
check_thread()
File "C:\PROGRA~1/SIDEEF~1/HOUDIN~1.724/houdini/python3.10libs\haio.py", line 112, in check_thread
raise RuntimeError("Current thread is not the main thread")
RuntimeError: Current thread is not the main thread
Hey @iiif
Just found something.
Apparently Houdini 20 ships with their own implementation of the asyncio event loop.
In Houdini 19.5 this feature could be enabled by setting the env variable:
HOUDINI_ASYNCIO=1
In Houdini 20 this seems to be enabled by default but can actually be turned off by setting the variable to 0. After this my asyncio code worked again.
Please note that I don't know if this is a good idea...
I'm sure this is the code i used within a dataclass for handling asynchronous websocket communication, making sure to run the new thread with `asyncio.run_coroutine_threadsafe()`, did the job, but I'll confirm later.
so I found a way to make it work both for houdini 19.5 and Houdini 20 and above.
You can control this with the CREATE_THREAD variable. In 19.5 the event loop needs to run in a separate thread otherwise it's blocking the UI.
The scripts tries to include all sorts of asyncio stuff, like endless coroutines, async generators, executing a last coroutine before houdini shuts down as well as gracefully ending the loop.
Save this in a pythonrc.py file and include it in HOUDINI_PATH so the loop gets started when houdini boots up.
Note: sheduling a coroutine in the atexit event leads to seg fault in houdini 20 and above.
importthreadingimportasyncioimportatexitimportlogginglogging.basicConfig(level=logging.DEBUG)logger=logging.getLogger("hou-asyncio")logger.setLevel(logging.DEBUG)print("Hello from pythonrc")CREATE_THREAD=Falsedefget_event_loop()->asyncio.AbstractEventLoop:try:loop=asyncio.get_running_loop()logger.info("Take existing event loop: %s",loop)exceptRuntimeError:loop=asyncio.new_event_loop()logger.info("Created new event loop: %s",loop)returnloopasyncdefasync_start_heartbeat():whileTrue:logger.info("Heartbeat")awaitasyncio.sleep(1.5)asyncdefasync_generator():number=0whileTrue:yieldnumbernumber+=1awaitasyncio.sleep(1)asyncdefasync_use_async_generator():asyncfornumberinasync_generator():logger.info("Received number: %i",number)asyncdefasync_cancel_all_tasks(loop:asyncio.AbstractEventLoop):tasks=[fortinasyncio.all_tasks(loop=loop)iftisnotasyncio.current_task(loop=loop)fortaskintasks:task.cancel()awaitasyncio.gather(*tasks,return_exceptions=True)asyncdefasync_shutdown_tasks(loop:asyncio.AbstractEventLoop):logger.info("Cancelling all tasks...")awaitasync_cancel_all_tasks(loop)logger.info("Shutting down async generators...")awaitloop.shutdown_asyncgens()asyncdefasync_last_coroutine():logger.info("Houdini about to exit, executing last coroutine...")foriinrange(3):logger.info("Last couroutine done in: %i",3-i)awaitasyncio.sleep(1)logger.info("Last coroutine done")defon_exit(loop:asyncio.AbstractEventLoop,thread:threading.Thread=None):logger.info("Detected houdini exit event")future=asyncio.run_coroutine_threadsafe(async_last_coroutine(),loop)future.result()future=asyncio.run_coroutine_threadsafe(async_shutdown_tasks(loop),loop)future.result()logger.info("Stopping event loop...")loop.call_soon_threadsafe(loop.stop)logger.info("Closing event loop...")loop.call_soon_threadsafe(loop.close)ifthread:thread.join()logger.info("Joined thread: %s",thread.native_id)def_start_loop_target(loop:asyncio.AbstractEventLoop):asyncio.set_event_loop(loop)loop.run_forever()defmain():loop=get_event_loop()asyncio.set_event_loop(loop)# No need to start the event loop in Houdini?thread=Noneifnotloop.is_running():ifCREATE_THREAD:thread=threading.Thread(target=_start_loop_target,args=(loop,))thread.start()logger.info("Starting event loop: %s in thread: %s",loop,thread.native_id)else:logger.info("Starting event loop: %s",loop)loop.run_forever()logger.info("Scheduling main coroutines...")asyncio.run_coroutine_threadsafe(async_start_heartbeat(),loop)asyncio.run_coroutine_threadsafe(async_use_async_generator(),loop)atexit.register(on_exit,loop,thread)logger.info("Registered atexit callback")if__name__=="__main__":main()