This release is a pre-release and may not be stable for production use.
Denobox
A Python library for executing JavaScript and WebAssembly in a Deno sandbox.
Overview
denobox provides a simple interface to run JavaScript code and WebAssembly modules from Python using Deno as the runtime. It communicates with a Deno subprocess using a newline-delimited JSON (NDJSON) protocol over stdin/stdout.
Features
- Execute JavaScript code in a fully sandboxed Deno environment
- Load and call WebAssembly modules
- Both synchronous and async APIs
- JSON-based data exchange between Python and JavaScript
- Promise resolution handled automatically
- Thread-safe (sync) and concurrent (async) execution support
Security
Deno runs with zero permissions - maximum sandboxing:
- No file system access - WASM files are read by Python and sent as base64
- No network access - cannot make HTTP requests or open sockets
- No subprocess spawning - cannot execute shell commands
- No environment access - cannot read environment variables
The sandbox is enforced by Deno's permission system. Any attempt to access restricted resources will raise an error.
Warning: The sandbox is only as secure as Deno itself. See Deno's documentation on executing untrusted code for important security considerations.
Installation
pip install denobox
Or with uv:
uv add denobox
Requirements
- Python 3.10+
- The
denoPyPI package (automatically installed)
Usage
JavaScript Execution
Synchronous API
from denobox import Denobox
with Denobox() as box:
# Simple expressions
result = box.eval("1 + 1")
print(result) # 2
# Strings
result = box.eval("'hello' + ' ' + 'world'")
print(result) # "hello world"
# Arrays and objects
result = box.eval("[1, 2, 3].map(x => x * 2)")
print(result) # [2, 4, 6]
result = box.eval("({name: 'test', value: 42})")
print(result) # {'name': 'test', 'value': 42}
# State persists between evals
box.eval("var x = 10")
box.eval("var y = 20")
result = box.eval("x + y")
print(result) # 30
# Promises are automatically resolved
result = box.eval("Promise.resolve(42)")
print(result) # 42
# Async functions work too
result = box.eval("(async () => { return 'async result'; })()")
print(result) # "async result"
# If you return only the function it will be automatically invoked
result = box.eval("async () => { return 'async result'; }")
print(result) # "async result"
Asynchronous API
import asyncio
from denobox import AsyncDenobox
async def main():
async with AsyncDenobox() as box:
result = await box.eval("1 + 1")
print(result) # 2
# Concurrent execution
results = await asyncio.gather(
box.eval("1 + 1"),
box.eval("2 + 2"),
box.eval("3 + 3"),
)
print(results) # [2, 4, 6]
asyncio.run(main())
WebAssembly
Synchronous API
from denobox import Denobox
with Denobox() as box:
# Load a WASM module from a file (Python reads and sends to Deno)
module = box.load_wasm("path/to/module.wasm")
# Or load from raw bytes
wasm_bytes = open("path/to/module.wasm", "rb").read()
module = box.load_wasm(wasm_bytes=wasm_bytes)
# Check available exports
print(module.exports) # {'add': 'function', 'multiply': 'function'}
# Call exported functions
result = module.call("add", 3, 4)
print(result) # 7
result = module.call("multiply", 5, 6)
print(result) # 30
# Unload when done (optional, cleaned up on box close)
module.unload()
Asynchronous API
import asyncio
from denobox import AsyncDenobox
async def main():
async with AsyncDenobox() as box:
module = await box.load_wasm("path/to/module.wasm")
# Concurrent calls
results = await asyncio.gather(
module.call("add", 1, 2),
module.call("add", 3, 4),
module.call("multiply", 5, 6),
)
print(results) # [3, 7, 30]
await module.unload()
asyncio.run(main())
Error Handling
from denobox import Denobox, DenoboxError
with Denobox() as box:
try:
box.eval("throw new Error('Something went wrong')")
except DenoboxError as e:
print(f"JavaScript error: {e}")
try:
box.eval("invalid javascript {{{")
except DenoboxError as e:
print(f"Syntax error: {e}")
Architecture
NDJSON Protocol
Communication between Python and the Deno subprocess uses newline-delimited JSON:
Requests:
{"id": 1, "type": "eval", "code": "1 + 1"}
{"id": 2, "type": "load_wasm", "bytes": "<base64-encoded-wasm>"}
{"id": 3, "type": "call_wasm", "moduleId": "wasm_0", "func": "add", "args": [1, 2]}
{"id": 4, "type": "unload_wasm", "moduleId": "wasm_0"}
{"id": 5, "type": "shutdown"}
Note: WASM modules are sent as base64-encoded bytes. Python reads the file and encodes it, so Deno doesn't need file system access.
Responses:
{"id": 1, "result": 2}
{"id": 2, "result": {"moduleId": "wasm_0", "exports": {"add": "function"}}}
{"id": 3, "result": 3}
{"id": 4, "result": true}
{"id": 5, "result": true, "shutdown": true}
Errors:
{"id": 1, "error": "ReferenceError: x is not defined", "stack": "..."}
Components
- Denobox - Synchronous wrapper using subprocess.Popen with thread-safe locking
- AsyncDenobox - Asynchronous wrapper using asyncio.create_subprocess_exec with a background reader task
- WasmModule / AsyncWasmModule - Wrappers for loaded WebAssembly modules
- worker.js - Deno script that handles the NDJSON protocol
Development
# Clone and setup
git clone <repo>
cd denobox
# Run tests
uv run pytest
# Run tests with verbose output
uv run pytest -v
License
Apache 2.0
Metadata
Release files for denobox 0.1a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| denobox-0.1a2.tar.gz | 16.4 kB | With provenance | Details for denobox-0.1a2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| denobox-0.1a2-py3-none-any.whl | Python 3 | none | any | Details for denobox-0.1a2-py3-none-any.whl |
Total release size: 30.5 kB
Release files / denobox-0.1a2.tar.gz
| Download URL | denobox-0.1a2.tar.gz |
|---|---|
| Size | 16.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
97b0dc2d467be0f74d23acf91c49d2e1cec6a42232b339cb3c0937b69f50b76d
|
|
BLAKE2b-256 checksum How to use checksums |
b31cd800d31a37c02d6ec6a62637fe48f77a8e606cc94b77b3762e0bb69152da
|
| 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 Jan 10, 2026.
Transparency logRelease files / denobox-0.1a2-py3-none-any.whl
| Download URL | denobox-0.1a2-py3-none-any.whl |
|---|---|
| Size | 14.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
198f1e35f459be674b1d4d8226a2aecb866d9fced2dbc01949299393e5060781
|
|
BLAKE2b-256 checksum How to use checksums |
e020b613612bc637ad9454ddd4ff17881a1896496f88dccf96dde4607e4b80f7
|
| 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 Jan 10, 2026.
Transparency logSecurity for denobox 0.1a2
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for denobox-0.1a2.tar.gz
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 Jan 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.