-
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added detection for Twisted #10
base: master
Are you sure you want to change the base?
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #10 +/- ##
==========================================
+ Coverage 96.51% 97.54% +1.02%
==========================================
Files 4 4
Lines 86 122 +36
Branches 17 22 +5
==========================================
+ Hits 83 119 +36
Misses 2 2
Partials 1 1
|
This looks pretty fragile... What if someone is using a reactor besides the global one so the I'd be more comfortable if we could get some feedback from the twisted devs. I know @hawkowl is working on adding context var support, which would make all this much easier... |
In a real application right now, there's too much code that exists that relies on the global reactor for it to not be installed. Removing that is the plan, but even when contextvars support lands, it'll be optional for Twisted (at least until 3.7 becomes the earliest we support). BTW, |
WRT the checking if it's running in the same thread -- Twisted's reactor is "threadsafe" in that |
@hawkowl There's a PyPI backport of The goal of this project is to make it so you can do The PR here uses stack introspection to see if the global reactor is running + it's inside The really big practical issue though is that if you're using So I guess doing stack introspection to check whether we're inside the |
The stack introspection approach was the only thing I could think of that would work. My first attempt involved
Is the plan going to be to have threadlocal reactors then, or just passing a reactor instance around? I really hope it's not the latter. Asyncio at least is doing away with the pattern of passing around the event loop object, and that is an improvement IMHO.
My thinking was along the lines of using the asyncio code path if Twisted is using the asyncio reactor. This should work, yes? I don't have enough Twisted experience to be sure though. |
My understanding is that the asyncio and twisted coroutine runners are currently incompatible, because of using different yield protocols. (asyncio yields I wouldn't be too shocked though if it turns out I misunderstood something. |
My understanding if you need to use asyncio coroutines or futures, you need to wrap them with |
This of course does mean that you can't just use asyncio code path as-is, but it could possibly be wrapped appropriately for Twisted. I'd be happy to make a native Twisted back-end, if that is deemed possible. |
BTW, what to do about the 3.8 test failure? It's just a consequence of Twisted using the deprecated |
Sure, but the whole point of sniffio is to figure out whether you're in an asyncio coroutine or a twisted coroutine.
The point is to make sure we notice this kind of issue first, before our users do. In this case, there's nothing for us to do about this warning, so we should just add a filter to ignore that warning. But unfortunately we have to look at it to know that; there's no way to know ahead of time which warnings are spurious. |
The code is way nicer now, but I think the behavior is still fundamentally broken if folks are mixing asyncio+twisted code in the same event loop, which seems like a very common use case, and increasingly common over time :-(. At the least we need a test that checks that different combinations of |
I'm not sure I understand. In what circumstances do you see that the check would return the wrong result? Can you give me a pseudocode example? |
async def aio_flavored():
assert sniffio.current_async_library() == "asyncio"
async def twisted_flavored():
assert sniffio.current_async_library() == "twisted"
await Deferred.fromFuture(asyncio.ensure_future(aio_flavored())
react(lambda _: ensureDeferred(twisted_flavored())) |
I just checked, and from within that Twisted coroutine, both |
Huh. That.... is not the problem I was expecting. |
I took a closer look at this and it appears that Twisted's |
AFAIK ...Oh, I guess the issue is that if you're using In the grand scheme of things though, I don't think this makes any difference, because there were already other reasons why introspecting the reactor didn't work :-). Basically we want |
I modified the def react(main, argv=(), _reactor=None):
if _reactor is None:
from twisted.internet import reactor as _reactor
codes = [0]
def runMain():
try:
finished = main(_reactor, *argv)
except BaseException as exc:
cbFinish(Failure(exc))
else:
finished.addBoth(cbFinish)
def cbFinish(result):
try:
_reactor.stop()
except ReactorNotRunning:
pass
if isinstance(result, Failure):
if result.check(SystemExit) is not None:
codes[0] = result.value.code
else:
log.err(result, "main function encountered error")
codes[0] = 1
_reactor.callWhenRunning(runMain)
_reactor.run()
sys.exit(codes[0]) |
No description provided.