A Rust-backed subprocess wrapper with split stdout/stderr streaming
Reason this release was yanked:
Broken
Project description
running-process
running-process is a Rust-backed subprocess runtime with a thin Python API.
The pipe-backed API keeps stdout and stderr separate, preserves raw bytes until decode time, and defaults to UTF-8 with errors="replace" when you ask for text. The PTY API is separate because terminal sessions are chunk-oriented and should not be forced through line normalization.
Pipe-backed API
from running_process import RunningProcess
process = RunningProcess(
["python", "-c", "import sys; print('out'); print('err', file=sys.stderr)"]
)
process.wait()
print(process.stdout) # stdout only
print(process.stderr) # stderr only
print(process.combined_output) # combined compatibility view
Captured data values stay plain str | bytes. Live stream handles are exposed separately:
if process.stdout_stream.available():
print(process.stdout_stream.drain())
Process priority is a first-class launch option:
from running_process import CpuPriority, RunningProcess
process = RunningProcess(
["python", "-c", "import time; time.sleep(1)"],
nice=CpuPriority.LOW,
)
nice= behavior:
- accepts either a raw
intniceness or a platform-neutralCpuPriority - on Unix, it maps directly to process niceness
- on Windows, positive values map to below-normal or idle priority classes and negative values map to above-normal or high priority classes
0leaves the default scheduler priority unchanged- positive values are the portable default; negative values may require elevated privileges
- the enum intentionally stops at
HIGH; there is no realtime tier
Available helpers:
get_next_stdout_line(timeout)get_next_stderr_line(timeout)get_next_line(timeout)for combined compatibility readsdrain_stdout()drain_stderr()drain_combined()stdout_stream.available()stderr_stream.available()combined_stream.available()
RunningProcess.run(...) supports common subprocess.run(...) style cases including:
capture_output=Truetext=Trueencoding=...errors=...shell=Trueenv=...nice=...stdin=subprocess.DEVNULLinput=...in text or bytes form
Unsupported subprocess.run(...) kwargs now fail loudly instead of being silently ignored.
Expect API
expect(...) is available on both the pipe-backed and PTY-backed process APIs.
import re
import subprocess
from running_process import RunningProcess
process = RunningProcess(
["python", "-c", "print('prompt>'); import sys; print('echo:' + sys.stdin.readline().strip())"],
stdin=subprocess.PIPE,
)
process.expect("prompt>", timeout=5, action="hello\n")
match = process.expect(re.compile(r"echo:(.+)"), timeout=5)
print(match.groups)
Supported action= forms:
strorbytes: write to stdin"interrupt": send Ctrl-C style interrupt when supported"terminate""kill"callable(match, process)
Pipe-backed expect(...) matches line-delimited output. If the child writes prompts without trailing newlines, use the PTY API instead.
PTY API
Use RunningProcess.pseudo_terminal(...) for interactive terminal sessions. It is chunk-oriented by design and preserves carriage returns and terminal control flow instead of normalizing it away.
from running_process import ExpectRule, RunningProcess
pty = RunningProcess.pseudo_terminal(
["python", "-c", "import sys; sys.stdout.write('name?'); sys.stdout.flush(); print('hello ' + sys.stdin.readline().strip())"],
text=True,
expect=[ExpectRule("name?", "world\n")],
expect_timeout=5,
)
print(pty.output)
PTY behavior:
- accepts
strandlist[str]commands - auto-splits simple string commands into argv when shell syntax is not present
- uses shell mode automatically when shell metacharacters are present
- keeps output chunk-buffered by default
- preserves
\rfor redraw-style terminal output - supports
write(...),read(...),drain(),available(),expect(...),resize(...), andsend_interrupt() - supports
nice=...at launch - supports
restore_callback=andcleanup_callback=so terminal restoration and cleanup paths are explicit and testable - supports
interrupt_and_wait(...)for staged interrupt escalation - supports
wait_for_idle(...)with activity filtering - exposes
exit_reason,interrupt_count,interrupted_by_caller, andexit_status
There is also a compatibility alias: RunningProcess.psuedo_terminal(...).
You can also inspect the intended interactive launch semantics without launching a child:
from running_process import RunningProcess
spec = RunningProcess.interactive_launch_spec("console_isolated")
print(spec.ctrl_c_owner)
print(spec.creationflags)
Supported launch specs:
pseudo_terminalconsole_sharedconsole_isolated
For an actual launch, use RunningProcess.interactive(...):
process = RunningProcess.interactive(
["python", "-c", "print('hello from interactive mode')"],
mode="console_shared",
nice=5,
)
process.wait()
Abnormal Exits
By default, nonzero exits stay subprocess-like: you get a return code and can inspect exit_status.
process = RunningProcess(["python", "-c", "import sys; sys.exit(3)"])
process.wait()
print(process.exit_status)
If you want abnormal exits to raise, opt in:
from running_process import ProcessAbnormalExit, RunningProcess
try:
RunningProcess.run(
["python", "-c", "import sys; sys.exit(3)"],
capture_output=True,
raise_on_abnormal_exit=True,
)
except ProcessAbnormalExit as exc:
print(exc.status.summary)
Notes:
- keyboard interrupts still raise
KeyboardInterrupt kill -9/SIGKILLis classified as an abnormal signal exit- possible OOM conditions are exposed as a hint on
exit_status.possible_oom - OOM cannot be identified perfectly across platforms from exit status alone, so it is best-effort rather than guaranteed
Text and bytes
Pipe mode is byte-safe internally:
- invalid UTF-8 does not break capture
- text mode decodes with UTF-8 and
errors="replace"by default - binary mode returns bytes unchanged
\r\nis normalized as a line break in pipe mode- bare
\ris preserved
PTY mode is intentionally more conservative:
- output is handled as chunks, not lines
- redraw-oriented
\ris preserved - no automatic terminal-output normalization is applied
Development
./install
./lint
./test
./install bootstraps the repo-local Rust toolchain into ./.cargo and ./.rustup.
./test runs the Rust tests, rebuilds the native extension with the unoptimized dev profile, runs the non-live Python tests, and then runs the @pytest.mark.live coverage that exercises real OS process and signal behavior.
If you want to invoke pytest directly, set RUNNING_PROCESS_LIVE_TESTS=1 and run uv run pytest -m live.
For direct Rust commands, prefer the repo-local trampolines:
./_cargo check --workspace
./_cargo fmt --all --check
./_cargo clippy --workspace --all-targets -- -D warnings
Notes
stdoutandstderrare no longer merged by default.combined_outputexists for compatibility when you need the merged view.RunningProcess(..., use_pty=True)is no longer the preferred path; useRunningProcess.pseudo_terminal(...)for PTY sessions.- The test suite checks that
running_process.__version__, package metadata, and manifest versions stay in sync.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file running_process-3.0.0.tar.gz.
File metadata
- Download URL: running_process-3.0.0.tar.gz
- Upload date:
- Size: 26.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7cd77387b879c85d2f6a08dc4124c359943e8c19c0a6408d62d0df73c5176c0
|
|
| MD5 |
461cab8957f4cf4b78fa45b087f2a107
|
|
| BLAKE2b-256 |
adaaf711397ee90bf0d28d10a8b45e3cb9878ba74ba011eb124abbcb0ebaac50
|
File details
Details for the file running_process-3.0.0-cp311-cp311-win_arm64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-win_arm64.whl
- Upload date:
- Size: 237.9 kB
- Tags: CPython 3.11, Windows ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dac2b44ea6c82df780832440ecee9c99512353c4c0d95868183ca7fb290ce3ee
|
|
| MD5 |
5107ee7405959d73560fc0ceeb741e54
|
|
| BLAKE2b-256 |
e46c003e9ee11cace2ac203d784821f91410463de821348f31cc79b5595fed7f
|
File details
Details for the file running_process-3.0.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 243.7 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e2e82477cbe0052f94b0591824f558cdfe01b543194b7340a5af3881f5837a7
|
|
| MD5 |
91ec3035f910daf6ac1aa744913c1ded
|
|
| BLAKE2b-256 |
9619e3d47d0f6f940fa032db9c6cc9fb019c7880d8499ce10b9207834b1aac68
|
File details
Details for the file running_process-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 337.3 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a33b8c04b2259562a51082d63adfa01511dd31bb8532512542bae901aa9c26cb
|
|
| MD5 |
9ca0aa88bdda4d1f9396e0d3cb6f5e09
|
|
| BLAKE2b-256 |
2f04939df90a01983a404204b2ba470679fe2432fbec79dc3e0a4d2cb183d3fa
|
File details
Details for the file running_process-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 325.7 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b484b2ad8ba804ad8e0a015e459424174be93e4205f680e220412320e19a14c
|
|
| MD5 |
89508ba5f4c38834565e54fe7e06a369
|
|
| BLAKE2b-256 |
00c7714bbf81d523a8c6bfaa7548aea92870338f04dd22fdad7643faabcef6e2
|
File details
Details for the file running_process-3.0.0-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 337.0 kB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f216bc33c0f57a8c1f91344cd592a44980437bff67742c5d019dfa13977569b2
|
|
| MD5 |
36d4e34684cd929b52d65461eab30128
|
|
| BLAKE2b-256 |
acf02b6d98dcfe26e0ca4f1b1bdd4654e73c9764080f84a48932c07056998389
|
File details
Details for the file running_process-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl.
File metadata
- Download URL: running_process-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl
- Upload date:
- Size: 341.3 kB
- Tags: CPython 3.11, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3155f256b9804fb23b4ea7696416fd997833ed348ea3477c5b689cf830243157
|
|
| MD5 |
c8a8fb88daf03ace806749a0d035b834
|
|
| BLAKE2b-256 |
1c9a5f1af7c21d4ac18330505d5b45c20280f7ebe0822f6f3667c6e97324b21b
|