SniffIO doesn't detect asyncio when running sync callbacks in loop
As discussed in https://github.com/agronholm/anyio/discussions/501 SniffIO doesn't detect it's running in asyncio when executing a callback added with loop.call_soon_threadsafe since it relies on asyncio.get_current_task for detection.
Using asyncio.get_running_loop should from my understanding work for all cases the current detection does and also allow detection in the case of executing in the event loop but not having a task.
I'll add a PR with the change implemented as well.
I know we discussed this in AnyIO @agronholm and you suggested trying to fix it here. Is there something I could do to make it easier to resolve this or is it a bad idea (regarding the linked MR)
What are you trying to achieve by having sniffio return something in particular when invoked from a sync callback? I tend to think of sniffio as being for answering the question "what library can I await things from right now", and from a sync callback the answer is "none of them". If we want it to answer the question "what library can I spawn tasks with right now", there's not necessarily even an unambiguous answer, because it's possible to run multiple event loops in the same thread using guest mode or trio-asyncio.
I'm not necessarily opposed to changing the behavior here. It would definitely be nice (and good for performance) to be able to set the sniffio loop once at the start of run, instead of setting and unsetting it around every task tick. But I want to make sure we have a coherent story for what sniffio's purpose is and how its semantics achieve that purpose, and I'm worried that supporting sniffing from a non-task context might muddy that.
@oremanj in my case it was just a to be able to allow anyio to create Events in the sync callback (which created structures containing them which would be awaited later later in the same loop by an async callback).
I realize you may not have that context on this level but in this case it was even from threads created by to_thread and then calling with from_thread.run_sync so in some sense they were already in an async context.
For my case this is mostly semantic and I could change the callback to async to allow the detection (but I, in particularly this case, like signaling that it's atomic with respect to the loop thread with no awaits by defining it sync).
For discussion if you want perspective in figuring it out my ,possibly inaccurate since I'm not testing right now, view is that this works in trio when called by from_thread since detection there is based on an explicit contextvar which I think trio propagates with the call while asyncio detection is based on get_running_task which fails since there is no task even if we're in fact explicitly running in an eventloop.
In the case of trio's guest mode it would indeed maybe give the wrong answer if the contextvar telling we're in fact in trio-context doesn't get passed and we fall back to get_current_loop and report asyncio for a sync callback scheduled from trio (but ran in asyncio).
I'm not sure how trio-asyncio works in this case but the semantics I would have wished was probably (in the case where we are in a callback scheduled with a particular libraries API that we get the library's flavour even if the loop for example is asyncio)
this works in trio when called by
from_threadsince detection there is based on an explicit contextvar which I think trio propagates with the call while asyncio detection is based onget_running_taskwhich fails since there is no task even if we're in fact explicitly running in an eventloop.
@tapetersen have you seen/had a chance to test https://github.com/agronholm/anyio/pull/524? i think the problem you encountered with anyio.from_thread.run_sync (https://github.com/agronholm/anyio/discussions/501) has been fixed downstream.
(that AnyIO fix doesn't actually address this post, but it should have addressed the AnyIO bug that prompted this post.)