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.
@@ -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
+ [![Linux x86](https://github.com/zackees/running-process/actions/workflows/linux-x86.yml/badge.svg)](https://github.com/zackees/running-process/actions/workflows/linux-x86.yml)
17
+ [![Linux ARM](https://github.com/zackees/running-process/actions/workflows/linux-arm.yml/badge.svg)](https://github.com/zackees/running-process/actions/workflows/linux-arm.yml)
18
+ [![Windows x86](https://github.com/zackees/running-process/actions/workflows/windows-x86.yml/badge.svg)](https://github.com/zackees/running-process/actions/workflows/windows-x86.yml)
19
+ [![Windows ARM](https://github.com/zackees/running-process/actions/workflows/windows-arm.yml/badge.svg)](https://github.com/zackees/running-process/actions/workflows/windows-arm.yml)
20
+ [![macOS x86](https://github.com/zackees/running-process/actions/workflows/macos-x86.yml/badge.svg)](https://github.com/zackees/running-process/actions/workflows/macos-x86.yml)
21
+ [![macOS ARM](https://github.com/zackees/running-process/actions/workflows/macos-arm.yml/badge.svg)](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
+