Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Denobox

PyPI Tests Changelog License

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 deno PyPI 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

  1. Denobox - Synchronous wrapper using subprocess.Popen with thread-safe locking
  2. AsyncDenobox - Asynchronous wrapper using asyncio.create_subprocess_exec with a background reader task
  3. WasmModule / AsyncWasmModule - Wrappers for loaded WebAssembly modules
  4. 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)

Source distribution for denobox 0.1a2
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)

Table of built distributions (wheels) for denobox 0.1a2
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page