Restricted Python execution runner with subprocess isolation, timeout, memory caps, and import/global controls
Project description
safe-py-runner
A lightweight, secure-by-default Python code runner designed for LLM agents.
The Missing Middleware for AI Agents: When building agents that write code, you often face a dilemma:
- Run Blindly: Use
exec()in your main process (Dangerous, fragile). - Full Sandbox: Spin up Docker containers for every execution (Heavy, slow, complex).
- SaaS: Pay for external sandbox APIs (Expensive, latency).
safe-py-runner offers a middle path: It runs code in a subprocess with timeout, memory limits, and input/output marshalling. It's perfect for internal tools, data analysis agents, and POCs where full Docker isolation is overkill.
Features
- 🛡️ Process Isolation: User code runs in a separate subprocess, protecting your main app from crashes.
- ⏱️ Timeouts: Automatically kill scripts that run too long (default 5s).
- 💾 Memory Limits: Enforce RAM usage caps (default 256MB) on POSIX systems.
- 🚫 Import Blocklist: Prevent access to dangerous modules (
os,subprocess,socket). - 📦 Magic I/O: Automatically injects input variables and captures results as JSON.
Installation
pip install safe-py-runner
Quick Start
from safe_py_runner import RunnerPolicy, run_code
# Define a policy (optional, defaults are safe)
policy = RunnerPolicy(
timeout_seconds=5,
memory_limit_mb=128,
blocked_imports=["os", "subprocess", "socket"],
)
# Run code
result = run_code(
code="import math\nresult = math.sqrt(input_data['x'])",
input_data={"x": 81},
policy=policy,
# Optional: Path to a specific Python executable (e.g., in a venv)
# python_executable="/path/to/venv/bin/python",
)
if result.ok:
print(f"Result: {result.result}") # 9.0
else:
print(f"Error: {result.error}")
## Advanced Configuration
### Using a Custom Python Environment
By default, `safe-py-runner` uses `sys.executable` (the same Python running your app).
To improve isolation or provide specific libraries to the runner, creating a dedicated virtual environment is recommended:
1. Create a venv: `python -m venv runner_env`
2. Install allowed packages: `runner_env/bin/pip install pandas numpy`
3. Pass the path to `run_code`:
```python
run_code(
code="...",
python_executable="/path/to/runner_env/bin/python"
)
## Security Note
**This is not an OS-level sandbox.**
It uses Python runtime hooks and resource limits to prevent accidents and basic misuse. For hosting code from anonymous/hostile users, you MUST pair this with Docker or similar isolation.
## Contributing
Contributions are welcome! Please open an issue or PR on GitHub.
## CI and Release Automation
- Push to `main`: runs CI tests automatically via GitHub Actions.
- Push a tag like `v0.1.1`: builds `sdist` + wheel, creates a GitHub Release, and publishes to PyPI via Trusted Publishing.
Release title/description are read from:
- `.github/release/metadata.json`
Trusted Publishing configuration expected by this repo:
- workflow name: `release`
- workflow file: `.github/workflows/release.yml`
- GitHub environment: `pypi`
Example `release_metadata.md`:
```md
# safe-py-runner {{tag}}
Release notes for {{tag}}.
- Summarize key changes here.
- Add migration notes if any.
{{tag}} is replaced automatically with the pushed tag name by the release workflow.
Commit Workflow (Step-by-Step)
Assuming commit_message.txt is already updated:
- Run tests:
make test
- Stage files:
make git-add
- Commit:
make git-commit
make git-commit validates:
commit_message.txtexists and is non-emptycommit_message.txtis different from the latest git commit message
- Push to
main:
make git-push
Optional shortcut:
make push
Note: if you already ran atomic steps (make git-add + make git-commit), you can run either make git-push or make push. make push is smart now: when there are no local file changes, it skips commit/test steps and only pushes.
Release Workflow (Step-by-Step)
Assuming commit_message.txt and release_metadata.md are updated:
- Bump version:
make set-version 0.1.2
- Regenerate release metadata JSON:
make metadata
- Run tests:
make test
- Stage files:
make git-add
- Commit:
make git-commit
- Push to
main:
make git-push
- Create and push release tag:
make git-tag
make git-tag reads version from pyproject.toml and will fail if:
- a version argument is provided
- version in
pyproject.tomlis missing or invalid - current tag from
pyproject.tomlis already the latest tag
Optional shortcut:
make release
Note: if you run release as atomic steps and already executed make git-commit, continue with make git-push and make git-tag (or run make push first; it will detect no local file changes and only push).
Additional Recommended Steps
- Run
make helpto see available commands. - Verify GitHub Actions CI passed on
mainbefore tagging. - After tagging, verify the release workflow succeeded and wheel artifact is attached.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 safe_py_runner-0.1.4.tar.gz.
File metadata
- Download URL: safe_py_runner-0.1.4.tar.gz
- Upload date:
- Size: 30.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ed8a9a3e6ff18ac60cfe6f1eac169eb230d1a62be885317cb4d7d76b749e2e1
|
|
| MD5 |
fc293288b4198970998e575695631a02
|
|
| BLAKE2b-256 |
42422e7184b2d41e3e4fed8be5024aa6f0a3c53070fa5cc904e311d760376e07
|
Provenance
The following attestation bundles were made for safe_py_runner-0.1.4.tar.gz:
Publisher:
release.yml on adarsh9780/safe-py-runner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safe_py_runner-0.1.4.tar.gz -
Subject digest:
7ed8a9a3e6ff18ac60cfe6f1eac169eb230d1a62be885317cb4d7d76b749e2e1 - Sigstore transparency entry: 982498371
- Sigstore integration time:
-
Permalink:
adarsh9780/safe-py-runner@6a13bb20fd2683366f0c83174c402de26c6c38b7 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/adarsh9780
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a13bb20fd2683366f0c83174c402de26c6c38b7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file safe_py_runner-0.1.4-py3-none-any.whl.
File metadata
- Download URL: safe_py_runner-0.1.4-py3-none-any.whl
- Upload date:
- Size: 8.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5064f97de3dc941c3eca31054917ba9483149e48fa4f4a0c77b4d3dd23cab899
|
|
| MD5 |
49e0da5a8a14e14f04e67b3aeafde944
|
|
| BLAKE2b-256 |
ea3ff33669629c339e945f99477e521086cf8a0b7b29b7a62c603080add3c0dd
|
Provenance
The following attestation bundles were made for safe_py_runner-0.1.4-py3-none-any.whl:
Publisher:
release.yml on adarsh9780/safe-py-runner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safe_py_runner-0.1.4-py3-none-any.whl -
Subject digest:
5064f97de3dc941c3eca31054917ba9483149e48fa4f4a0c77b4d3dd23cab899 - Sigstore transparency entry: 982498426
- Sigstore integration time:
-
Permalink:
adarsh9780/safe-py-runner@6a13bb20fd2683366f0c83174c402de26c6c38b7 -
Branch / Tag:
refs/tags/v0.1.4 - Owner: https://github.com/adarsh9780
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a13bb20fd2683366f0c83174c402de26c6c38b7 -
Trigger Event:
push
-
Statement type: