running_process 3.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- running_process-3.0.0/Cargo.lock +209 -0
- running_process-3.0.0/Cargo.toml +15 -0
- running_process-3.0.0/LICENSE +21 -0
- running_process-3.0.0/PKG-INFO +261 -0
- running_process-3.0.0/README.md +247 -0
- running_process-3.0.0/crates/running-process-core/Cargo.toml +13 -0
- running_process-3.0.0/crates/running-process-core/src/lib.rs +560 -0
- running_process-3.0.0/crates/running-process-core/tests/process_core_test.rs +141 -0
- running_process-3.0.0/crates/running-process-py/Cargo.toml +17 -0
- running_process-3.0.0/crates/running-process-py/src/lib.rs +276 -0
- running_process-3.0.0/pyproject.toml +42 -0
- running_process-3.0.0/src/running_process/__init__.py +53 -0
- running_process-3.0.0/src/running_process/assets/example.txt +1 -0
- running_process-3.0.0/src/running_process/exit_status.py +100 -0
- running_process-3.0.0/src/running_process/expect.py +76 -0
- running_process-3.0.0/src/running_process/line_iterator.py +34 -0
- running_process-3.0.0/src/running_process/output_formatter.py +40 -0
- running_process-3.0.0/src/running_process/priority.py +28 -0
- running_process-3.0.0/src/running_process/process_utils.py +33 -0
- running_process-3.0.0/src/running_process/pty.py +815 -0
- running_process-3.0.0/src/running_process/running_process.py +922 -0
- running_process-3.0.0/src/running_process/running_process_manager.py +50 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.4"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "heck"
|
|
19
|
+
version = "0.5.0"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "indoc"
|
|
25
|
+
version = "2.0.7"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
28
|
+
dependencies = [
|
|
29
|
+
"rustversion",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "libc"
|
|
34
|
+
version = "0.2.184"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "memoffset"
|
|
40
|
+
version = "0.9.1"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
43
|
+
dependencies = [
|
|
44
|
+
"autocfg",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[[package]]
|
|
48
|
+
name = "once_cell"
|
|
49
|
+
version = "1.21.4"
|
|
50
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
51
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
52
|
+
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "portable-atomic"
|
|
55
|
+
version = "1.13.1"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "proc-macro2"
|
|
61
|
+
version = "1.0.106"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"unicode-ident",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "pyo3"
|
|
70
|
+
version = "0.23.5"
|
|
71
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
+
checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
|
|
73
|
+
dependencies = [
|
|
74
|
+
"cfg-if",
|
|
75
|
+
"indoc",
|
|
76
|
+
"libc",
|
|
77
|
+
"memoffset",
|
|
78
|
+
"once_cell",
|
|
79
|
+
"portable-atomic",
|
|
80
|
+
"pyo3-build-config",
|
|
81
|
+
"pyo3-ffi",
|
|
82
|
+
"pyo3-macros",
|
|
83
|
+
"unindent",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[[package]]
|
|
87
|
+
name = "pyo3-build-config"
|
|
88
|
+
version = "0.23.5"
|
|
89
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
|
+
checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
|
|
91
|
+
dependencies = [
|
|
92
|
+
"once_cell",
|
|
93
|
+
"target-lexicon",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[[package]]
|
|
97
|
+
name = "pyo3-ffi"
|
|
98
|
+
version = "0.23.5"
|
|
99
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
100
|
+
checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
|
|
101
|
+
dependencies = [
|
|
102
|
+
"libc",
|
|
103
|
+
"pyo3-build-config",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[[package]]
|
|
107
|
+
name = "pyo3-macros"
|
|
108
|
+
version = "0.23.5"
|
|
109
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
110
|
+
checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
|
|
111
|
+
dependencies = [
|
|
112
|
+
"proc-macro2",
|
|
113
|
+
"pyo3-macros-backend",
|
|
114
|
+
"quote",
|
|
115
|
+
"syn",
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[[package]]
|
|
119
|
+
name = "pyo3-macros-backend"
|
|
120
|
+
version = "0.23.5"
|
|
121
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
122
|
+
checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
|
|
123
|
+
dependencies = [
|
|
124
|
+
"heck",
|
|
125
|
+
"proc-macro2",
|
|
126
|
+
"pyo3-build-config",
|
|
127
|
+
"quote",
|
|
128
|
+
"syn",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
[[package]]
|
|
132
|
+
name = "quote"
|
|
133
|
+
version = "1.0.45"
|
|
134
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
135
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
136
|
+
dependencies = [
|
|
137
|
+
"proc-macro2",
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
[[package]]
|
|
141
|
+
name = "running-process-core"
|
|
142
|
+
version = "3.0.0"
|
|
143
|
+
dependencies = [
|
|
144
|
+
"libc",
|
|
145
|
+
"thiserror",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
[[package]]
|
|
149
|
+
name = "running-process-py"
|
|
150
|
+
version = "3.0.0"
|
|
151
|
+
dependencies = [
|
|
152
|
+
"pyo3",
|
|
153
|
+
"running-process-core",
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
[[package]]
|
|
157
|
+
name = "rustversion"
|
|
158
|
+
version = "1.0.22"
|
|
159
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
160
|
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
161
|
+
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "syn"
|
|
164
|
+
version = "2.0.117"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
167
|
+
dependencies = [
|
|
168
|
+
"proc-macro2",
|
|
169
|
+
"quote",
|
|
170
|
+
"unicode-ident",
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
[[package]]
|
|
174
|
+
name = "target-lexicon"
|
|
175
|
+
version = "0.12.16"
|
|
176
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
177
|
+
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|
178
|
+
|
|
179
|
+
[[package]]
|
|
180
|
+
name = "thiserror"
|
|
181
|
+
version = "2.0.18"
|
|
182
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
183
|
+
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
|
184
|
+
dependencies = [
|
|
185
|
+
"thiserror-impl",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[[package]]
|
|
189
|
+
name = "thiserror-impl"
|
|
190
|
+
version = "2.0.18"
|
|
191
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
192
|
+
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
|
193
|
+
dependencies = [
|
|
194
|
+
"proc-macro2",
|
|
195
|
+
"quote",
|
|
196
|
+
"syn",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[[package]]
|
|
200
|
+
name = "unicode-ident"
|
|
201
|
+
version = "1.0.24"
|
|
202
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
204
|
+
|
|
205
|
+
[[package]]
|
|
206
|
+
name = "unindent"
|
|
207
|
+
version = "0.2.4"
|
|
208
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
209
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
resolver = "2"
|
|
3
|
+
members = ["crates/running-process-core", "crates/running-process-py"]
|
|
4
|
+
|
|
5
|
+
[workspace.package]
|
|
6
|
+
version = "3.0.0"
|
|
7
|
+
edition = "2021"
|
|
8
|
+
rust-version = "1.85"
|
|
9
|
+
license = "BSD-3-Clause"
|
|
10
|
+
repository = "https://github.com/zackees/running-process"
|
|
11
|
+
homepage = "https://github.com/zackees/running-process"
|
|
12
|
+
|
|
13
|
+
[workspace.dependencies]
|
|
14
|
+
pyo3 = { version = "0.23", features = ["extension-module"] }
|
|
15
|
+
thiserror = "2"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 zackees
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: running_process
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Requires-Dist: psutil>=5.9.0
|
|
5
|
+
Requires-Dist: pywinpty>=3.0.0 ; platform_system == 'Windows'
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Summary: A Rust-backed subprocess wrapper with split stdout/stderr streaming
|
|
8
|
+
Home-Page: https://github.com/zackees/running-process
|
|
9
|
+
Author-email: Your Name <your.email@example.com>
|
|
10
|
+
License: BSD 3-Clause License
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
13
|
+
|
|
14
|
+
# running-process
|
|
15
|
+
|
|
16
|
+
[](https://github.com/zackees/running-process/actions/workflows/linux-x86.yml)
|
|
17
|
+
[](https://github.com/zackees/running-process/actions/workflows/linux-arm.yml)
|
|
18
|
+
[](https://github.com/zackees/running-process/actions/workflows/windows-x86.yml)
|
|
19
|
+
[](https://github.com/zackees/running-process/actions/workflows/windows-arm.yml)
|
|
20
|
+
[](https://github.com/zackees/running-process/actions/workflows/macos-x86.yml)
|
|
21
|
+
[](https://github.com/zackees/running-process/actions/workflows/macos-arm.yml)
|
|
22
|
+
|
|
23
|
+
`running-process` is a Rust-backed subprocess runtime with a thin Python API.
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
## Pipe-backed API
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from running_process import RunningProcess
|
|
31
|
+
|
|
32
|
+
process = RunningProcess(
|
|
33
|
+
["python", "-c", "import sys; print('out'); print('err', file=sys.stderr)"]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
process.wait()
|
|
37
|
+
|
|
38
|
+
print(process.stdout) # stdout only
|
|
39
|
+
print(process.stderr) # stderr only
|
|
40
|
+
print(process.combined_output) # combined compatibility view
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Captured data values stay plain `str | bytes`. Live stream handles are exposed separately:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
if process.stdout_stream.available():
|
|
47
|
+
print(process.stdout_stream.drain())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Process priority is a first-class launch option:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from running_process import CpuPriority, RunningProcess
|
|
54
|
+
|
|
55
|
+
process = RunningProcess(
|
|
56
|
+
["python", "-c", "import time; time.sleep(1)"],
|
|
57
|
+
nice=CpuPriority.LOW,
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`nice=` behavior:
|
|
62
|
+
|
|
63
|
+
- accepts either a raw `int` niceness or a platform-neutral `CpuPriority`
|
|
64
|
+
- on Unix, it maps directly to process niceness
|
|
65
|
+
- on Windows, positive values map to below-normal or idle priority classes and negative values map to above-normal or high priority classes
|
|
66
|
+
- `0` leaves the default scheduler priority unchanged
|
|
67
|
+
- positive values are the portable default; negative values may require elevated privileges
|
|
68
|
+
- the enum intentionally stops at `HIGH`; there is no realtime tier
|
|
69
|
+
|
|
70
|
+
Available helpers:
|
|
71
|
+
|
|
72
|
+
- `get_next_stdout_line(timeout)`
|
|
73
|
+
- `get_next_stderr_line(timeout)`
|
|
74
|
+
- `get_next_line(timeout)` for combined compatibility reads
|
|
75
|
+
- `drain_stdout()`
|
|
76
|
+
- `drain_stderr()`
|
|
77
|
+
- `drain_combined()`
|
|
78
|
+
- `stdout_stream.available()`
|
|
79
|
+
- `stderr_stream.available()`
|
|
80
|
+
- `combined_stream.available()`
|
|
81
|
+
|
|
82
|
+
`RunningProcess.run(...)` supports common `subprocess.run(...)` style cases including:
|
|
83
|
+
|
|
84
|
+
- `capture_output=True`
|
|
85
|
+
- `text=True`
|
|
86
|
+
- `encoding=...`
|
|
87
|
+
- `errors=...`
|
|
88
|
+
- `shell=True`
|
|
89
|
+
- `env=...`
|
|
90
|
+
- `nice=...`
|
|
91
|
+
- `stdin=subprocess.DEVNULL`
|
|
92
|
+
- `input=...` in text or bytes form
|
|
93
|
+
|
|
94
|
+
Unsupported `subprocess.run(...)` kwargs now fail loudly instead of being silently ignored.
|
|
95
|
+
|
|
96
|
+
## Expect API
|
|
97
|
+
|
|
98
|
+
`expect(...)` is available on both the pipe-backed and PTY-backed process APIs.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import re
|
|
102
|
+
import subprocess
|
|
103
|
+
from running_process import RunningProcess
|
|
104
|
+
|
|
105
|
+
process = RunningProcess(
|
|
106
|
+
["python", "-c", "print('prompt>'); import sys; print('echo:' + sys.stdin.readline().strip())"],
|
|
107
|
+
stdin=subprocess.PIPE,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
process.expect("prompt>", timeout=5, action="hello\n")
|
|
111
|
+
match = process.expect(re.compile(r"echo:(.+)"), timeout=5)
|
|
112
|
+
print(match.groups)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Supported `action=` forms:
|
|
116
|
+
|
|
117
|
+
- `str` or `bytes`: write to stdin
|
|
118
|
+
- `"interrupt"`: send Ctrl-C style interrupt when supported
|
|
119
|
+
- `"terminate"`
|
|
120
|
+
- `"kill"`
|
|
121
|
+
- `callable(match, process)`
|
|
122
|
+
|
|
123
|
+
Pipe-backed `expect(...)` matches line-delimited output. If the child writes prompts without trailing newlines, use the PTY API instead.
|
|
124
|
+
|
|
125
|
+
## PTY API
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from running_process import ExpectRule, RunningProcess
|
|
131
|
+
|
|
132
|
+
pty = RunningProcess.pseudo_terminal(
|
|
133
|
+
["python", "-c", "import sys; sys.stdout.write('name?'); sys.stdout.flush(); print('hello ' + sys.stdin.readline().strip())"],
|
|
134
|
+
text=True,
|
|
135
|
+
expect=[ExpectRule("name?", "world\n")],
|
|
136
|
+
expect_timeout=5,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
print(pty.output)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
PTY behavior:
|
|
143
|
+
|
|
144
|
+
- accepts `str` and `list[str]` commands
|
|
145
|
+
- auto-splits simple string commands into argv when shell syntax is not present
|
|
146
|
+
- uses shell mode automatically when shell metacharacters are present
|
|
147
|
+
- keeps output chunk-buffered by default
|
|
148
|
+
- preserves `\r` for redraw-style terminal output
|
|
149
|
+
- supports `write(...)`, `read(...)`, `drain()`, `available()`, `expect(...)`, `resize(...)`, and `send_interrupt()`
|
|
150
|
+
- supports `nice=...` at launch
|
|
151
|
+
- supports `restore_callback=` and `cleanup_callback=` so terminal restoration and cleanup paths are explicit and testable
|
|
152
|
+
- supports `interrupt_and_wait(...)` for staged interrupt escalation
|
|
153
|
+
- supports `wait_for_idle(...)` with activity filtering
|
|
154
|
+
- exposes `exit_reason`, `interrupt_count`, `interrupted_by_caller`, and `exit_status`
|
|
155
|
+
|
|
156
|
+
There is also a compatibility alias: `RunningProcess.psuedo_terminal(...)`.
|
|
157
|
+
|
|
158
|
+
You can also inspect the intended interactive launch semantics without launching a child:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from running_process import RunningProcess
|
|
162
|
+
|
|
163
|
+
spec = RunningProcess.interactive_launch_spec("console_isolated")
|
|
164
|
+
print(spec.ctrl_c_owner)
|
|
165
|
+
print(spec.creationflags)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Supported launch specs:
|
|
169
|
+
|
|
170
|
+
- `pseudo_terminal`
|
|
171
|
+
- `console_shared`
|
|
172
|
+
- `console_isolated`
|
|
173
|
+
|
|
174
|
+
For an actual launch, use `RunningProcess.interactive(...)`:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
process = RunningProcess.interactive(
|
|
178
|
+
["python", "-c", "print('hello from interactive mode')"],
|
|
179
|
+
mode="console_shared",
|
|
180
|
+
nice=5,
|
|
181
|
+
)
|
|
182
|
+
process.wait()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Abnormal Exits
|
|
186
|
+
|
|
187
|
+
By default, nonzero exits stay subprocess-like: you get a return code and can inspect `exit_status`.
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
process = RunningProcess(["python", "-c", "import sys; sys.exit(3)"])
|
|
191
|
+
process.wait()
|
|
192
|
+
print(process.exit_status)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
If you want abnormal exits to raise, opt in:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from running_process import ProcessAbnormalExit, RunningProcess
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
RunningProcess.run(
|
|
202
|
+
["python", "-c", "import sys; sys.exit(3)"],
|
|
203
|
+
capture_output=True,
|
|
204
|
+
raise_on_abnormal_exit=True,
|
|
205
|
+
)
|
|
206
|
+
except ProcessAbnormalExit as exc:
|
|
207
|
+
print(exc.status.summary)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Notes:
|
|
211
|
+
|
|
212
|
+
- keyboard interrupts still raise `KeyboardInterrupt`
|
|
213
|
+
- `kill -9` / `SIGKILL` is classified as an abnormal signal exit
|
|
214
|
+
- possible OOM conditions are exposed as a hint on `exit_status.possible_oom`
|
|
215
|
+
- OOM cannot be identified perfectly across platforms from exit status alone, so it is best-effort rather than guaranteed
|
|
216
|
+
|
|
217
|
+
## Text and bytes
|
|
218
|
+
|
|
219
|
+
Pipe mode is byte-safe internally:
|
|
220
|
+
|
|
221
|
+
- invalid UTF-8 does not break capture
|
|
222
|
+
- text mode decodes with UTF-8 and `errors="replace"` by default
|
|
223
|
+
- binary mode returns bytes unchanged
|
|
224
|
+
- `\r\n` is normalized as a line break in pipe mode
|
|
225
|
+
- bare `\r` is preserved
|
|
226
|
+
|
|
227
|
+
PTY mode is intentionally more conservative:
|
|
228
|
+
|
|
229
|
+
- output is handled as chunks, not lines
|
|
230
|
+
- redraw-oriented `\r` is preserved
|
|
231
|
+
- no automatic terminal-output normalization is applied
|
|
232
|
+
|
|
233
|
+
## Development
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
./install
|
|
237
|
+
./lint
|
|
238
|
+
./test
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`./install` bootstraps the repo-local Rust toolchain into `./.cargo` and `./.rustup`.
|
|
242
|
+
|
|
243
|
+
`./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.
|
|
244
|
+
|
|
245
|
+
If you want to invoke pytest directly, set `RUNNING_PROCESS_LIVE_TESTS=1` and run `uv run pytest -m live`.
|
|
246
|
+
|
|
247
|
+
For direct Rust commands, prefer the repo-local trampolines:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
./_cargo check --workspace
|
|
251
|
+
./_cargo fmt --all --check
|
|
252
|
+
./_cargo clippy --workspace --all-targets -- -D warnings
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Notes
|
|
256
|
+
|
|
257
|
+
- `stdout` and `stderr` are no longer merged by default.
|
|
258
|
+
- `combined_output` exists for compatibility when you need the merged view.
|
|
259
|
+
- `RunningProcess(..., use_pty=True)` is no longer the preferred path; use `RunningProcess.pseudo_terminal(...)` for PTY sessions.
|
|
260
|
+
- The test suite checks that `running_process.__version__`, package metadata, and manifest versions stay in sync.
|
|
261
|
+
|