Skip to content
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 API for changing the context of a running task. #2086

Closed
wants to merge 5 commits into from
Closed

Add API for changing the context of a running task. #2086

wants to merge 5 commits into from

Conversation

Fuyukai
Copy link
Member

@Fuyukai Fuyukai commented Aug 1, 2021

This is implemented as an asynchronous context manager (allowing arbitrary nesting).

@codecov
Copy link

codecov bot commented Aug 1, 2021

Codecov Report

Merging #2086 (24b3814) into master (f8cb817) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##           master    #2086   +/-   ##
=======================================
  Coverage   99.55%   99.55%           
=======================================
  Files         114      116    +2     
  Lines       14751    14785   +34     
  Branches     2341     2344    +3     
=======================================
+ Hits        14685    14719   +34     
  Misses         44       44           
  Partials       22       22           
Impacted Files Coverage Δ
trio/lowlevel.py 100.00% <ø> (ø)
trio/_core/__init__.py 100.00% <100.00%> (ø)
trio/_core/_context.py 100.00% <100.00%> (ø)
trio/_core/tests/test_context.py 100.00% <100.00%> (ø)

@smurfix
Copy link
Contributor

smurfix commented Aug 1, 2021

Hmm, I'd call this function changed_context so that the with statement reads like a reasonable English phrase.

@Fuyukai
Copy link
Member Author

Fuyukai commented Aug 1, 2021

That's inconsistent with every other context manager in the library.

@Fuyukai
Copy link
Member Author

Fuyukai commented Aug 4, 2021

If there's no objections to this in the next four days I'm going to merge this unilaterally.

Copy link
Member

@goodboy goodboy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me; any lower level testing is just testing contextvars itself 😂

I wonder would an example of usage in a real trio example be of note?
I can't think of where I'd use this offhand so it'd be handy to have a notion up front where you'd expect to have it used.

Copy link
Member

@oremanj oremanj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the change! I have some mixed feelings about the "I'm going to commit this regardless of review once it's been up for a week" -- I think based on typical review habits in this project that a week of silence doesn't actually constitute a mandate to go ahead. I can't deny it prompted me to review ASAP, but it felt a little coercive? I'd be more open to this sort of thing if we agree on a standard.

@@ -0,0 +1,2 @@
Add an API to allow changing the current Context a task is running
in.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link to the new API here? And cross-reference contextvars.Context

trio/__init__.py Outdated
@@ -32,6 +32,7 @@
BrokenResourceError,
EndOfChannel,
Nursery,
change_context,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the public export of this belongs in lowlevel

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was very unsure if this should be in public or lowlevel, I basically did a mental coin flip. I'll change it back.


@asynccontextmanager
async def change_context(context):
"""Asynchronous context manager to change the :class:`contextvars.Context`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest "Returns an async context manager that can be used to change the ... to match the style of the docs on other functions that do that

task.context = context

try:
await _run.checkpoint()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should both be cancel_shielded_checkpoint in case the user wants to change the context without taking a cancellation. This is a pretty low-level feature so I'm not worried about deviation from the "entry and exit to ACM are checkpoints" rule. (This is probably more justifiable if you take the suggestion to put the function in lowlevel.)

Regardless, you should document what happens on entry and on exit wrt cancellation and scheduling.

yield
finally:
task.context = saved_context
# I assume this is right?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entry and exit are symmetric IMO; you need a schedule point to pick up the new context, but there aren't any correctness implications I can see about whether it should also be a cancel point or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second one is cancel-shielded because it would produce a second cancellation event if the stuff inside was cancelled. The first one isn't because it seems better to not have to do two round trips through the scheduler if the task is already cancelled.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair -- I guess anyone who wants to change the context without taking a cancellation can also use a shielded scope. I think most permutations here are defensible as long as they're documented.



@asynccontextmanager
async def change_context(context):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps change_current_context or set_current_context to make the functionality clearer from the name?

@@ -1020,6 +1020,12 @@ Example output (yours may differ slightly):
request 0: Helper task b finished
request 0: Request received finished

You can change the current :class:`contextvars.Context` a task is running
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels out of place when the prior discussion didn't even mention the concept of a Context (only discussed context variables). That's part of why I think this might do better in lowlevel. We have #1543 that can keep it company (once cleaned up and landed) in some sort of "Low-level context tools" section.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll move it to the low-level docs as its own section.

@Fuyukai
Copy link
Member Author

Fuyukai commented Aug 4, 2021

I have some mixed feelings about the "I'm going to commit this regardless of review once it's been up for a week" -- I think based on typical review habits in this project that a week of silence doesn't actually constitute a mandate to go ahead.

This feature is blocking the project I'm working on. Given that it's a ten line feature change, relatively simple, and that the review was primarily about semantics I think that it's fair to expect this could be pushed through quickly.

@oremanj
Copy link
Member

oremanj commented Aug 4, 2021

This feature is blocking the project I'm working on. Given that it's a ten line feature change, relatively simple, and that the review was primarily about semantics I think that it's fair to expect this could be pushed through quickly.

You could inline change_context() into your project in the meantime -- it doesn't actually use any private APIs. I'm not trying to pass judgment on whether pushing on this change's timeline was abstractly fair or not; mostly wanted to flag that I was personally somewhat taken aback by the approach.

@pquentin
Copy link
Member

@Fuyukai any progress on this?

@Fuyukai
Copy link
Member Author

Fuyukai commented Dec 23, 2021

Hi, I picked this back up again after ~five months of being a writer :)

I addressed all the comments raised by @oremanj. I also wish to apologise for my rash behaviour and threatening a unilateral merge.

@Fuyukai Fuyukai closed this by deleting the head repository Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants