Bug Description
The async client (NewAClient) is broken on Python 3.12+ due to event_global_loop being a separate event loop from the one created by asyncio.run(). This causes a RuntimeError: Task got Future attached to a different loop when calling await client.idle().
Steps to Reproduce
import asyncio
from neonize.aioze.client import NewAClient
from neonize.aioze.events import ConnectedEv
async def main():
client = NewAClient("test_bot")
@client.event(ConnectedEv)
async def on_connected(client, event):
print("Connected!")
await client.connect()
await client.idle() # RuntimeError here
asyncio.run(main())
Error:
RuntimeError: Task <Task pending ...> got Future <Task pending coro=<to_thread() ...>> attached to a different loop
Root Cause
In neonize/aioze/events.py line 52:
event_global_loop = asyncio.new_event_loop()
This creates a separate event loop at import time. Then in NewAClient.__init__:
self.loop = event_global_loop
And in connect():
self.connect_task = self.loop.create_task(task) # task created on event_global_loop
When idle() does await self.connect_task, it tries to await a task from event_global_loop while running on the loop created by asyncio.run() — which raises RuntimeError on Python 3.12+.
Additionally, __onQr and Event.execute use asyncio.run_coroutine_threadsafe(..., event_global_loop) to schedule callbacks. Since event_global_loop is never started (no run_forever()), these coroutines are scheduled but never executed — so QR codes never display and event handlers never fire.
Suggested Fix
In connect(), use the running loop instead of event_global_loop:
async def connect(self):
self.loop = asyncio.get_running_loop()
# ... rest of connect
And update execute() and __onQr to reference self.loop (or the module-level variable) consistently, ensuring it points to the running loop.
Current Workaround
Patch both module-level references before creating the client:
import neonize.aioze.client as neonize_client
import neonize.aioze.events as neonize_events
loop = asyncio.get_running_loop()
neonize_events.event_global_loop = loop
neonize_client.event_global_loop = loop
client = NewAClient("bot")
client.loop = loop
Both modules must be patched because client.py imports its own copy via from .events import event_global_loop.
Secondary Issue: QR handler dispatch
@client.event(QREv) registers in list_func, but QR events are dispatched through event._qr via __onQr, not through execute(). Users must use @client.qr instead, which isn't obvious from the API.
Environment
- neonize 0.3.14.post0
- Python 3.13
- macOS (Apple Silicon)
Happy to submit a PR with the fix if you'd like.
Bug Description
The async client (
NewAClient) is broken on Python 3.12+ due toevent_global_loopbeing a separate event loop from the one created byasyncio.run(). This causes aRuntimeError: Task got Future attached to a different loopwhen callingawait client.idle().Steps to Reproduce
Error:
Root Cause
In
neonize/aioze/events.pyline 52:This creates a separate event loop at import time. Then in
NewAClient.__init__:And in
connect():When
idle()doesawait self.connect_task, it tries to await a task fromevent_global_loopwhile running on the loop created byasyncio.run()— which raisesRuntimeErroron Python 3.12+.Additionally,
__onQrandEvent.executeuseasyncio.run_coroutine_threadsafe(..., event_global_loop)to schedule callbacks. Sinceevent_global_loopis never started (norun_forever()), these coroutines are scheduled but never executed — so QR codes never display and event handlers never fire.Suggested Fix
In
connect(), use the running loop instead ofevent_global_loop:And update
execute()and__onQrto referenceself.loop(or the module-level variable) consistently, ensuring it points to the running loop.Current Workaround
Patch both module-level references before creating the client:
Both modules must be patched because
client.pyimports its own copy viafrom .events import event_global_loop.Secondary Issue: QR handler dispatch
@client.event(QREv)registers inlist_func, but QR events are dispatched throughevent._qrvia__onQr, not throughexecute(). Users must use@client.qrinstead, which isn't obvious from the API.Environment
Happy to submit a PR with the fix if you'd like.