bounded_subprocess 2.4.0__tar.gz → 2.5.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.
- bounded_subprocess-2.5.0/AGENTS.md +10 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/PKG-INFO +1 -1
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/pyproject.toml +1 -1
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/bounded_subprocess_async.py +25 -2
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/test_async.py +45 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/uv.lock +1 -1
- bounded_subprocess-2.4.0/AGENTS.md +0 -5
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/.git +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/.github/workflows/docs.yaml +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/.github/workflows/test.yml +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/.gitignore +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/LICENSE.txt +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/Makefile +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/README.md +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/cspell.config.yaml +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/docs/index.md +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/mkdocs.yaml +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/__init__.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/bounded_subprocess.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/interactive.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/interactive_async.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/util.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/__init__.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/block_on_inputs.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/close_outputs.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/dies_shortly_after_launch.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/dies_while_writing.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/does_not_read.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/echo_stdin.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/fork_bomb.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/fork_once.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/long_stdout.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/read_one_line.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/sleep_forever.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/unbounded_output.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/write_forever_but_no_newline.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/module_test.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/test_interactive.py +0 -0
- {bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/test_interactive_async.py +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Use `uv run`to run and not `python`.
|
|
2
|
+
|
|
3
|
+
## Publishing Process
|
|
4
|
+
|
|
5
|
+
Increment the version number as appropriate in pyproject.toml. Delete old
|
|
6
|
+
builds from dist/. Run uv sync to update the lock file. Commit the changes,
|
|
7
|
+
which should have just changes to pyproject.taml and uv.lock. Run `uv build`
|
|
8
|
+
and then `uv publish`. In the interactive prompt, you MUST enter __token__
|
|
9
|
+
for the username. I know it says that, but I don't read what's on the screen.
|
|
10
|
+
The password is in the keychain.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bounded_subprocess
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: A library to facilitate running subprocesses that may misbehave.
|
|
5
5
|
Project-URL: Homepage, https://github.com/arjunguha/bounded_subprocess
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/arjunguha/bounded_subprocess
|
|
@@ -163,6 +163,8 @@ async def podman_run(
|
|
|
163
163
|
env=None,
|
|
164
164
|
stdin_data: Optional[str] = None,
|
|
165
165
|
stdin_write_timeout: Optional[int] = None,
|
|
166
|
+
volumes: List[str] = [],
|
|
167
|
+
cwd: Optional[str] = None,
|
|
166
168
|
) -> Result:
|
|
167
169
|
"""
|
|
168
170
|
Run a subprocess in a podman container asynchronously with bounded stdout/stderr capture.
|
|
@@ -172,19 +174,32 @@ async def podman_run(
|
|
|
172
174
|
the same as `run`, except for an additional `image` parameter to specify the
|
|
173
175
|
container image to use.
|
|
174
176
|
|
|
177
|
+
Args:
|
|
178
|
+
args: Command arguments to run in the container.
|
|
179
|
+
image: Container image to use.
|
|
180
|
+
timeout_seconds: Maximum time to wait for the process to complete.
|
|
181
|
+
max_output_size: Maximum size in bytes for stdout/stderr capture.
|
|
182
|
+
env: Optional dictionary of environment variables.
|
|
183
|
+
stdin_data: Optional string data to write to stdin.
|
|
184
|
+
stdin_write_timeout: Optional timeout for writing stdin data.
|
|
185
|
+
volumes: Optional list of volume mount specifications (e.g., ["/host/path:/container/path"]).
|
|
186
|
+
cwd: Optional working directory path inside the container.
|
|
187
|
+
|
|
175
188
|
Example:
|
|
176
189
|
|
|
177
190
|
```python
|
|
178
191
|
import asyncio
|
|
179
|
-
from bounded_subprocess.bounded_subprocess_async import
|
|
192
|
+
from bounded_subprocess.bounded_subprocess_async import podman_run
|
|
180
193
|
|
|
181
194
|
async def main():
|
|
182
|
-
result = await
|
|
195
|
+
result = await podman_run(
|
|
183
196
|
["cat"],
|
|
184
197
|
image="alpine:latest",
|
|
185
198
|
timeout_seconds=5,
|
|
186
199
|
max_output_size=1024,
|
|
187
200
|
stdin_data="hello\n",
|
|
201
|
+
volumes=["/host/data:/container/data"],
|
|
202
|
+
cwd="/container/data",
|
|
188
203
|
)
|
|
189
204
|
print(result.exit_code)
|
|
190
205
|
print(result.stdout.strip())
|
|
@@ -209,6 +224,14 @@ async def podman_run(
|
|
|
209
224
|
for key, value in env.items():
|
|
210
225
|
podman_args.extend(["-e", f"{key}={value}"])
|
|
211
226
|
|
|
227
|
+
# Handle volume mounts
|
|
228
|
+
for volume in volumes:
|
|
229
|
+
podman_args.extend(["-v", volume])
|
|
230
|
+
|
|
231
|
+
# Handle working directory
|
|
232
|
+
if cwd is not None:
|
|
233
|
+
podman_args.extend(["-w", cwd])
|
|
234
|
+
|
|
212
235
|
podman_args.append(image)
|
|
213
236
|
podman_args.extend(args)
|
|
214
237
|
|
|
@@ -171,3 +171,48 @@ async def test_podman_run_unbounded_output():
|
|
|
171
171
|
assert result.timeout is True
|
|
172
172
|
# stderr may contain podman pull messages, which is fine
|
|
173
173
|
assert len(result.stdout) == 1024
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@pytest.mark.asyncio
|
|
177
|
+
async def test_podman_run_volumes():
|
|
178
|
+
"""Test podman_run with volume mounts."""
|
|
179
|
+
import tempfile
|
|
180
|
+
|
|
181
|
+
# Create a temporary file with test content
|
|
182
|
+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as tmp_file:
|
|
183
|
+
tmp_file.write("test content\n")
|
|
184
|
+
tmp_path = tmp_file.name
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
# Mount the file and read it from the container
|
|
188
|
+
result = await podman_run(
|
|
189
|
+
["cat", "/mnt/test.txt"],
|
|
190
|
+
image="alpine:latest",
|
|
191
|
+
timeout_seconds=5,
|
|
192
|
+
max_output_size=1024,
|
|
193
|
+
volumes=[f"{tmp_path}:/mnt/test.txt:ro"],
|
|
194
|
+
)
|
|
195
|
+
assert result.exit_code == 0
|
|
196
|
+
assert result.timeout is False
|
|
197
|
+
assert result.stdout == "test content\n"
|
|
198
|
+
finally:
|
|
199
|
+
# Clean up
|
|
200
|
+
try:
|
|
201
|
+
os.unlink(tmp_path)
|
|
202
|
+
except OSError:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@pytest.mark.asyncio
|
|
207
|
+
async def test_podman_run_cwd():
|
|
208
|
+
"""Test podman_run with working directory."""
|
|
209
|
+
result = await podman_run(
|
|
210
|
+
["pwd"],
|
|
211
|
+
image="alpine:latest",
|
|
212
|
+
timeout_seconds=5,
|
|
213
|
+
max_output_size=1024,
|
|
214
|
+
cwd="/tmp",
|
|
215
|
+
)
|
|
216
|
+
assert result.exit_code == 0
|
|
217
|
+
assert result.timeout is False
|
|
218
|
+
assert result.stdout.strip() == "/tmp"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/bounded_subprocess.py
RENAMED
|
File without changes
|
|
File without changes
|
{bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/src/bounded_subprocess/interactive_async.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/dies_while_writing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bounded_subprocess-2.4.0 → bounded_subprocess-2.5.0}/test/evil_programs/unbounded_output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|