-
Notifications
You must be signed in to change notification settings - Fork 248
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
Add the ability to modify a greenlet's contextvars context #198
Conversation
I didn't actually look at the pseudocode in #183 while I was writing this, so there are some semantic differences:
I'm happy to change either of these if you prefer the approach that was sketched in #183. (I didn't add a constructor argument either, but I figure that can be done in a separate PR if it's desired.) |
Thanks very much for this! I obviously like this basic approach, but I'm still wrapping my head around the issue(s), and it would be nice to get some more feedback from those involved in sqlalchemy/sqlalchemy#5615 and #183 (I'll solicit that feedback). I do have some responses to your comments:
What I was trying to do with that suggestion was militate against the fact that greenlets have I expect consumers like sqlalchemy that want to inherit the context after spawning to write code like this: glet = greenlet(…)
glet.gr_context = get_inherited_context()
… Since "errors should never pass silently," I don't think that should work on versions of greenlet without context support. I think (having thought a bit more 😄) that it should always raise (Because of the use of a compile-time-define that can override the default compile-time-Python-version check, one can't assume that if you're running on Python 3.7+ greenlet will have context support. Because they're not documented, I doubt many people would be aware of the fact that the compile-time-define is exposed in the Because of the way greenlet happens to be implemented on CPython, one could check for the attribute on the type of the greenlet: glet = greenlet(…)
if hasattr(type(glet), 'gr_context'):
glet.gr_context = get_inherited_context()
… But no one does that. They might check the instance, but that breaks down if anyone forgets to do the check and just assigns (because it goes in the So to be safe, I think glet = greenlet(…)
try:
glet.gr_context = get_inherited_context()
except AttributeError:
pass It's clearly not perfect because it doesn't work on older versions of greenlet, but I don't know what to do about that. Checking the type isn't portable, checking the module compile-time-define constant isn't documented, and checking the
I'm fine with that. A constructor argument is partial mitigation against what I described above, but it's not a complete solution either.
Those seem like they should be two distinct operations to me. It's not clear why they should be synonyms. (It's always easier to relax the behaviour later if necessary.) |
hey there - I'm not deeply familiar with contextvars as of yet but can you clarify what the source of |
|
I guess that's accurate. @fantix can you sign off on that approach ? |
This PR looks good to me, thanks for getting this done! @zzzeek I think this is good enough and I would be able to update sqlalchemy/sqlalchemy#5616 to use this PR in a cleaner way. |
I agree with the @zzzeek and @fantix
The use case is basically to use greenlets to execute async code from a sync caller. More context is here #173 and https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef |
b2390f9
to
6d76111
Compare
… 'del gr.gr_context'
6d76111
to
1f83f62
Compare
Thanks for merging! I was just looking at this again and I think the notes I wrote in the docs about I'll work on a docs PR to fix this, but it might not be for a few days. |
This PR adds
gr_context
as a writable greenlet attribute as suggested in the comments of #183. Unlike my proposal in #196, it is fully backward-compatible. I still think the default that was chosen in 0.4.17 is unfortunate, but I suspect this PR will require less bikeshedding than #196, and it allows those who wish to replicate the pre-0.4.17 behavior to do so.I chose to make
gr_context
accessible and modifiable even for the greenlet that is currently running in your thread (though not for greenlets that are running in other threads), because that seemed far less confusing than the alternative. This effectively makes it a direct accessor/mutator forPyThreadState::context
, which is not otherwise exposed to Python code. I think that's OK, since that value is just going to be stored inPyGreenlet::context
later anyhow -- modifyinggr_context
of the running greenlet doesn't let you do anything you couldn't do by modifyinggr_context
of a non-running greenlet. Using either ability carelessly can cause the exit fromContext.run()
to raise an exception, but nothing worse than that AFAICT.