safe-py-runner
A lightweight Python code runner with guardrails for LLM agent workflows.
Why This Package
When building agents that execute generated Python code, you often choose between:
- Running code directly in your process (
exec) - risky. - Full container sandboxing - heavier and slower.
- External sandbox APIs - added cost and latency.
safe-py-runner provides a practical middle path:
- subprocess isolation
- timeout enforcement
- memory limits (POSIX; macOS enforcement can be weaker than Linux)
- import/builtin policy restrictions
- JSON-safe input/output handling
Honest scope:
- Good fit: LLM-generated scripts for your own team and other controlled internal workloads.
- Not good alone: anonymous public code execution. Use Docker/VM/OS sandboxing as the primary boundary.
Where It Fits
| Option | Isolation Strength | Operational Cost | Typical Use |
|---|---|---|---|
eval / exec in main process |
Low | Low | Local scripts, trusted experiments |
safe-py-runner |
Medium | Low to medium | Internal tools, agent prototypes, controlled workloads |
| Docker / VM / E2B-style sandbox | High | Medium to high | Production multi-tenant or hostile untrusted code |
Production guidance:
- For hostile public-user code, use Docker/VM/external sandboxing as the primary boundary.
safe-py-runnercan be used as an extra inner guardrail layer.
It supports two policy modes:
restrict(default): block selected symbols.allow: allow only selected symbols.
Backends
The API now uses explicit engine objects through run_code(..., engine=...).
LocalEngine: host subprocess execution in managed venv.DockerEngine: managed Docker container execution.
If DockerEngine is used but Docker is unavailable, execution fails with a clear error.
DockerEngine can also target remote daemons with docker_context or SSH options.
Docker installation:
- Docker Desktop (macOS/Windows): https://www.docker.com/products/docker-desktop/
- Docker Engine docs (Linux): https://docs.docker.com/engine/install/
For production workloads, prefer DockerEngine over LocalEngine.
Installation
pip install safe-py-runner
Quick Start
from safe_py_runner import LocalEngine, RunnerPolicy, run_code
policy = RunnerPolicy(
timeout_seconds=5,
memory_limit_mb=128,
blocked_imports=["os", "subprocess", "socket"],
)
engine = LocalEngine(venv_dir="/tmp/safe_py_runner_pypi_demo")
result = run_code(
code="import math\nresult = math.sqrt(input_data['x'])",
input_data={"x": 81},
policy=policy,
engine=engine,
)
# Or load policy from a TOML config file
result = run_code(
code="result = 1 + 1",
policy_file="/absolute/path/to/policy.toml",
engine=engine,
)
if result.ok:
print(result.result) # 9.0
else:
print(result.error)
Security Note
This is not an OS-level sandbox.
For untrusted hostile code, use container/VM isolation in addition to this package.
Memory-limit caveat: RLIMIT_AS is platform-dependent. On macOS, address-space limits may not behave as strictly as Linux.
Common Gotchas
engineis required forrun_code(...); there is no implicit backend selection.policyandpolicy_fileare mutually exclusive; pass only one.- In
allowmode,allowed_globalscontrols both input key injection andextra_globals. importlibis intentionally blocked in all modes.- Package environments require pinned specs (
name==version).
More Information
- Full documentation and contributor workflow: README.md
- Security policy: SECURITY.md
- Issue tracker: GitHub Issues
Metadata
Release files for safe-py-runner 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| safe_py_runner-0.1.7.tar.gz | 58.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| safe_py_runner-0.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.0 kB
Release files / safe_py_runner-0.1.7.tar.gz
| Download URL | safe_py_runner-0.1.7.tar.gz |
|---|---|
| Size | 58.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1153384884a3e1e4bf0e59f0ab4dcbd19639eebbda550949c33dab6304293887
|
|
BLAKE2b-256 checksum How to use checksums |
128932df97bb5de1d0f973a0ef6ff7289d81dc44f57ce9c0157d297abdcd9e0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.
Transparency logRelease files / safe_py_runner-0.1.7-py3-none-any.whl
| Download URL | safe_py_runner-0.1.7-py3-none-any.whl |
|---|---|
| Size | 27.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9699089926aafe058bbcb6a76cdacdae8fdfc3961ab130f27a964aa4e3528b02
|
|
BLAKE2b-256 checksum How to use checksums |
ec7834911e39f66620ee496f5d8d2167d9005babedd984466b47d6aebb1547f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 26, 2026.
Transparency log