Genorators using database_sync_to_async are called on the main thread
This is probably true for the other decorators as well. Same issue if you call the decorators directly.
threading.get_ident() is the same as the async code when the function uses yield. If you change it to return a list it has a different ID.
Heres the code
@sync_to_async
def function_foo(self):
print(threading.get_ident(), 'function')
return 4
@sync_to_async
def generator_foo(self):
print(threading.get_ident(), 'generator')
yield 4
async def receive_json(self, content):
print(threading.get_ident(), 'loop')
print(await self.function_foo())
for i in await self.generator_foo():
print(i)
Heres the output
139945948370688 loop
139945849284352 function
4
139945948370688 generator
4
Finally getting back to this (I must have let it slip through last year, sorry).
You're totally right about this - we should see if there's a way to either detect this and raise an error, or intercept it and actually run it properly.
import threading
class YourClass: def function_foo(self): print(threading.get_ident(), 'function') return 4
async def generator_foo(self):
print(threading.get_ident(), 'generator')
yield 4
async def receive_json(self, content):
print(threading.get_ident(), 'loop')
print(self.function_foo()) # Call the synchronous function directly
async for i in self.generator_foo(): # Use 'async for' to iterate over the asynchronous generator
print(i)
I Guess this will work
import asyncio
import threading
from asgiref.sync import sync_to_async
@sync_to_async
def function_foo():
print(threading.get_ident(), 'function')
return 4
def run_in_thread(func):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
result = loop.run_until_complete(func())
loop.close()
return result
async def generator_foo():
print(threading.get_ident(), 'generator')
return await sync_to_async(lambda: 4)()
async def receive_json(content):
print(threading.get_ident(), 'loop')
print(await function_foo())
# Run the generator part in a separate thread
result = run_in_thread(generator_foo)
print(result)
Here is what I think could also work. The key addition is the run_in_thread function, which is used to execute the generator part (generator_foo) in a separate thread with its own event loop. This ensures a clear separation between asynchronous and synchronous code execution.