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

process = await nursery.start(run_process, ...) #1568

Merged
merged 16 commits into from
Oct 13, 2021

Conversation

njsmith
Copy link
Member

@njsmith njsmith commented Jun 2, 2020

Fixes #1104

Todo:

  • docs
  • tests
  • newsfragment
  • deprecate Process.aclose
  • move open_process to trio.lowlevel

I think this gets the basic semantics right? Still need to do
everything around that though.

@codecov
Copy link

codecov bot commented Jun 2, 2020

Codecov Report

Merging #1568 (1c4c99c) into master (f2e2312) will decrease coverage by 0.14%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master    #1568      +/-   ##
==========================================
- Coverage   99.56%   99.41%   -0.15%     
==========================================
  Files         114      114              
  Lines       14634    15404     +770     
  Branches     1119     1279     +160     
==========================================
+ Hits        14570    15314     +744     
- Misses         43       60      +17     
- Partials       21       30       +9     
Impacted Files Coverage Δ
trio/__init__.py 100.00% <100.00%> (ø)
trio/_subprocess.py 96.86% <100.00%> (-0.73%) ⬇️
trio/_subprocess_platform/__init__.py 100.00% <100.00%> (ø)
trio/lowlevel.py 100.00% <100.00%> (ø)
trio/tests/test_subprocess.py 100.00% <100.00%> (ø)
trio/_core/_multierror.py 94.09% <0.00%> (-4.81%) ⬇️
trio/socket.py 96.22% <0.00%> (-3.78%) ⬇️
trio/_core/_run.py 99.44% <0.00%> (-0.56%) ⬇️
trio/_core/tests/test_multierror.py 99.16% <0.00%> (-0.11%) ⬇️
... and 1 more

@oremanj
Copy link
Member

oremanj commented Jun 4, 2020

This looks like a good approach so far!

async with await trio.open_process(...) as process:
...
If you need more control – for example, because you want to spawn a child
process that outlives your program – then you can use `open_process`::
Copy link
Member

Choose a reason for hiding this comment

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

Wait is this true?

Can open_process() outlive the "program" given the subprocess routine is launched using .to_thread.run()`?

Maybe "program" isn't the ideal term here?

Copy link
Member Author

Choose a reason for hiding this comment

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

We use a thread to spin up the process, but once it's started it's an independent entity.

Copy link
Member

@goodboy goodboy Jul 13, 2021

Choose a reason for hiding this comment

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

Right, my quiff is on the use of the term program.
I presume no thread outlives the trio program given the deamon=True flag is used iirc?

Maybe I'm misunderstanding terminology.

Copy link
Member Author

Choose a reason for hiding this comment

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

open_process doesn't spawn a thread, it spawns a process... there are no threads here (except very briefly as an implementation detail that isn't mentioned in the docs)

Copy link
Member

Choose a reason for hiding this comment

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

Ahh i see. I didn't realize you could intentionally leak processes; gtk.

task_status.started(proc)
await proc.wait()
except BaseException:
with trio.CancelScope(shield=True):
Copy link
Member

@goodboy goodboy Dec 14, 2020

Choose a reason for hiding this comment

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

Yeah this is great; love how the spawning maps 1-to-1 with a task now.

This is exactly what we ended up with in tractor since we also needed a shielded Process.wait() before the call to Process.__aexit__() (internal IPC protocol takes care of inter-process cancellation) and the task mapping pattern was the only way to reliably collect sub-process errors and get cancellation logic right (even when using multiprocessing).

So the only motivation now for open_process() use directly is a non-SC process-lives-longer then "current nursery" type scenario yes? This run_process() call (presuming .start() use) is effectively the same but with guaranteed shielded supervision on teardown.

My only last question is if the async context mng interface is deprecated then is there now no longer a way to manually kill only a specific process?

Afaict here, you can't force the teardown (634 onwards) without cancelling the task (but that usually requires cancelling a group scope since, nursery, and a new cancel scope isn't created internally here per process). So if cancellation is the new __aexit__(), then we somewhat lose granular control of per-process cancellation and/or its completion detection right? Unless of course you want to use open_process() directly (but then in that case shouldn't __aexit__() code get moved to the post-yield body of open_process())?

The streams closing and process killing in .aclose() is something we currently depend on in tractor.

With that last question, forgive me if I'm missing something.

Copy link
Member Author

Choose a reason for hiding this comment

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

My only last question is if the async context mng interface is deprecated then is there now no longer a way to manually kill only a specific process?

You can still kill a process. Process.terminate/Process.kill/Process.send_signal are all still available, and you can still close the streams if you want. Process.aclose was always just a convenience wrapper around other existing public APIs, and that hasn't changed.

Copy link
Member

Choose a reason for hiding this comment

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

Process.aclose was always just a convenience wrapper around other existing public APIs, and that hasn't changed.

Ok so it will henceforth follow that users can roll their own version of .aclose() but that won't be default behavior.

@shamrin
Copy link
Contributor

shamrin commented Jan 5, 2021

@njsmith What is the current status of this PR? Now that I've stumbled upon open_process() weirdness in #1844, I'd rather try to help here rather than work-around open_process() in my experiments.

@njsmith
Copy link
Member Author

njsmith commented Jul 14, 2021

At long last, this is ready for review!

Copy link
Contributor

@richardsheridan richardsheridan left a comment

Choose a reason for hiding this comment

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

Happy to say goodbye to async with await! My comments relate mainly to documentation; the code changes look good to me for whatever that's worth 😉.

trio/_subprocess.py Outdated Show resolved Hide resolved
trio/_subprocess_platform/__init__.py Show resolved Hide resolved
trio/_subprocess.py Outdated Show resolved Hide resolved
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.

This looks great, thank you for finishing it up! Just a few small comments

docs/source/reference-io.rst Outdated Show resolved Hide resolved
trio/_subprocess.py Outdated Show resolved Hide resolved
trio/_subprocess.py Show resolved Hide resolved
@njsmith
Copy link
Member Author

njsmith commented Sep 19, 2021

The 3.10-dev failure appears to be some kind of incompatibility between IPython and 3.10-dev, nothing in our code.

@oremanj oremanj merged commit 0c2998f into python-trio:master Oct 13, 2021
@goodboy goodboy mentioned this pull request Dec 18, 2022
4 tasks
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.

Should we have a conventional way to "spawn a process into a nursery"?
5 participants