feat: more providers - #21
Conversation
There was a problem hiding this comment.
Pull request overview
Adds E2B, CoreWeave, and Cloudflare sandbox providers across the API, CLI, management tooling, packaging, tests, examples, and documentation.
Changes:
- Adds three provider implementations and registrations.
- Adds managers, CLI support, optional dependencies, and tests.
- Updates examples, documentation, and release notes.
Reviewed changes
Copilot reviewed 46 out of 46 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Summary |
|---|---|
tests/test_manage.py |
Managed provider registration coverage. |
tests/test_e2b.py |
E2B execution and integration tests. |
tests/test_coreweave.py |
CoreWeave driver and fallback tests. |
tests/test_cloudflare.py |
Cloudflare bridge protocol tests. |
README.md |
Lists new providers. |
pyproject.toml |
Adds optional dependencies and lint configuration. |
examples/repl/Makefile |
Adds REPL targets. |
examples/repl/e2b_sandbox_example.py |
E2B REPL example. |
examples/repl/coreweave_sandbox_example.py |
CoreWeave REPL example. |
examples/repl/cloudflare_sandbox_example.py |
Cloudflare REPL example. |
examples/README.md |
Documents example setup and requirements. |
examples/exec/Makefile |
Adds executable targets. |
examples/exec/e2b_sandbox_example.py |
E2B execution example. |
examples/exec/coreweave_sandbox_example.py |
CoreWeave execution example. |
examples/exec/cloudflare_sandbox_example.py |
Cloudflare execution example. |
docs/docs/sandboxes/monty.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/modal.mdx |
Documents Modal state persistence. |
docs/docs/sandboxes/kaggle.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/jupyter-server.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/index.mdx |
Documents variants and persistence. |
docs/docs/sandboxes/google-colab.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/eval.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/e2b.mdx |
Adds E2B documentation. |
docs/docs/sandboxes/docker.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/daytona.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/datalayer.mdx |
Updates sidebar ordering. |
docs/docs/sandboxes/coreweave.mdx |
Adds CoreWeave documentation. |
docs/docs/sandboxes/cloudflare.mdx |
Adds Cloudflare documentation. |
docs/docs/installation/index.mdx |
Documents extras and credentials. |
docs/docs/index.mdx |
Updates the provider overview. |
docs/docs/examples/index.mdx |
Adds example links. |
docs/docs/comparison/index.mdx |
Updates provider comparison. |
docs/docs/cli/management.mdx |
Documents provider management. |
docs/docs/cli/index.mdx |
Documents CLI support for new variants. |
docs/docs/api-reference/index.mdx |
Updates factory API documentation. |
code_sandboxes/providers.py |
Registers provider metadata and requirements. |
code_sandboxes/models.py |
Adds provider variants. |
code_sandboxes/modal_sandbox.py |
Updates Modal behavior documentation. |
code_sandboxes/manage.py |
Adds provider managers. |
code_sandboxes/e2b_sandbox.py |
Implements the E2B adapter. |
code_sandboxes/coreweave_sandbox.py |
Implements CoreWeave execution and fallback; variable APIs can falsely succeed without a driver, and timed-out executions continue running. |
code_sandboxes/cloudflare_sandbox.py |
Implements the Cloudflare bridge; configured environment variables and network policies are not applied, variable reads fail across fresh processes, and requests send a redacted placeholder instead of the API key. |
code_sandboxes/cli.py |
Registers variants and validates GPU options. |
code_sandboxes/base.py |
Updates factory and environment registration. |
code_sandboxes/__init__.py |
Exports new sandbox classes. |
CHANGELOG.md |
Records provider additions and behavior updates. |
Suppressed comments (17)
code_sandboxes/cloudflare_sandbox.py:231
SandboxConfig.max_lifetimeis a common sandbox setting and is forwarded by the E2B and CoreWeave implementations, but this create call sends no lifetime and there is no local enforcement. For example,max_lifetime=60can produce a Cloudflare sandbox that remains alive beyond the requested bound. If the bridge cannot configure lifetime, reject non-default values rather than silently ignoring them.
response = self._client.post("/v1/sandbox")
code_sandboxes/cloudflare_sandbox.py:104
- This
BaseExceptionhandler also catchesSystemExit, so code such assys.exit(2)is serialized as acode_errorand the runner process exits with status 0. The resultingExecutionResultcannot expose the non-zero exit code promised by the shared model; handleSystemExitseparately and carry its numeric code through the reply/result.
except BaseException as error:
reply["status"] = "error"
reply["error"] = {
code_sandboxes/cloudflare_sandbox.py:611
- When
envsis supplied, this prependedimportmakes valid snippets beginning withfrom __future__ import ...fail with “future feature ... is not defined” because future imports must be first. Inject the environment after the future-import block or execute the original snippet as a separately compiled unit.
return f"import os as _code_sandboxes_os\n{assignments}del _code_sandboxes_os\n{code}"
code_sandboxes/coreweave_sandbox.py:522
- When the session driver disappears,
_driver_requestreturnsNoneand this path silently switches to stateless execution, butsandbox.info.metadata["stateful"]remainsTrue. Callers following the documented capability check will keep assuming definitions persist after they have already been lost; update the metadata when the fallback is selected.
reply = self._driver_request(prepared, seconds)
if reply is None:
reply = self._stateless_request(prepared, seconds)
code_sandboxes/coreweave_sandbox.py:99
- The session driver catches
SystemExitas an ordinary code error, sosys.exit(2)returnscode_error=SystemExit, keeps the session alive, and never suppliesExecutionResult.exit_code. This disagrees with the shared model's exit-code contract; handleSystemExitseparately and include its numeric code in the reply/result.
except BaseException as error:
reply["status"] = "error"
reply["error"] = {
"name": type(error).__name__,
"value": str(error),
code_sandboxes/coreweave_sandbox.py:522
- A
Nonereply also covers the case where the driver accepted and began the request but died before emitting its response. Falling back to_stateless_requestthen executes arbitrary user code a second time, duplicating side effects such as file writes or external requests. Do not retry after a request may have been accepted; report the lost reply and restart the session instead.
reply = self._driver_request(prepared, seconds)
if reply is None:
reply = self._stateless_request(prepared, seconds)
code_sandboxes/coreweave_sandbox.py:452
- The stateless fallback is not actually compatible with the shared variable-based helpers.
_stateless_requestdiscards its namespace when it returns, so aftercommands.run()orfiles.list()executes a snippet, the subsequentget_variable()call runs in a new process and sees none of the result. The fallback is documented as “working, merely stateless”; return helper values from the same process or provide provider-specific command/filesystem implementations.
def _stateless_request(self, code: str, timeout: float) -> dict:
"""One snippet in a process of its own, for when there is no driver."""
process = self._sandbox.exec(
[self._python_executable, "-u", "-c", _STATELESS_SOURCE],
code_sandboxes/coreweave_sandbox.py:294
- The explicit
api_keyis written to process-globalos.environand never restored. Starting or managing a second CoreWeave sandbox with another key overwrites the first credential, so later SDK calls can authenticate against the wrong account (and the secret also leaks to unrelated code in this process). Serialize/restore credential use or use an SDK client/configuration that keeps the token per instance.
import os
if self._api_key:
os.environ["CWSANDBOX_API_KEY"] = self._api_key
if self._base_url:
os.environ["CWSANDBOX_BASE_URL"] = self._base_url
code_sandboxes/coreweave_sandbox.py:276
Sandbox.create(memory=...)storesmemory_limitin bytes, butResourceConfig.memoryis documented as MB. This passes bytes into the public resource metadata, so CoreWeave reports an incorrect memory value even though its request conversion below is correct.
resources=ResourceConfig(
cpu=self.config.cpu_limit,
memory=self.config.memory_limit,
gpu=self.config.gpu,
),
code_sandboxes/coreweave_sandbox.py:446
- The persistent driver protocol is shared by all callers, but
_driver_seq,stdin, and the reply queue are accessed without synchronization. Two concurrentrun_codecalls can interleave requests; both then compare against the mutable latest sequence and can consume/misassociate each other's replies or time out. Serialize a complete driver request (or track replies per request) before writing to the shared process.
self._driver_seq += 1
try:
self._driver.stdin.writeline(json.dumps({"seq": self._driver_seq, "code": code}))
except Exception:
logger.warning("The CoreWeave session driver went away; restarting stateless.")
self._driver = None
return None
deadline = time.monotonic() + timeout
while True:
remaining = deadline - time.monotonic()
if remaining <= 0:
raise TimeoutError(f"No reply from the CoreWeave session within {timeout:.0f}s.")
try:
line = self._driver_replies.get(timeout=remaining)
except queue.Empty:
continue
if line is None:
# The reader reached EOF: the driver is gone.
self._driver = None
return None
try:
reply = json.loads(line)
except ValueError:
continue
if reply.get("seq") == self._driver_seq:
code_sandboxes/coreweave_sandbox.py:696
- When
envsis supplied, this prependedimportmakes valid snippets beginning withfrom __future__ import ...fail with “future feature ... is not defined” because future imports must be first. Inject the environment after the future-import block or execute the original snippet as a separately compiled unit.
return f"import os as _code_sandboxes_os\n{assignments}del _code_sandboxes_os\n{code}"
code_sandboxes/e2b_sandbox.py:224
- When
memory=is supplied,Sandbox.createstores it inSandboxConfig.memory_limitas bytes, whileResourceConfig.memoryis documented as MB. Passing the byte value through makessandbox.info.resources.memoryreport a value 1,048,576 times too large; convert to MB before constructing this metadata.
resources=ResourceConfig(
cpu=self.config.cpu_limit,
memory=self.config.memory_limit,
),
code_sandboxes/manage.py:1096
- The detached manager drops the HTTP client without closing it. Each
createcall therefore leaves anhttpx.Clientand its connection pool open in the caller even though the remote sandbox remains detached; close the client before discarding the local reference.
sandbox._client = None
sandbox._started = False
docs/docs/cli/index.mdx:103
- The CLI does pass
--gputhrough for CoreWeave:_GPU_VARIANTSincludescoreweaveand_resolve_variant_kwargsaddsgpu. This sentence tells users the opposite, despite the example supporting--gpu; document the actual CLI behavior instead.
so lines share a namespace. `--gpu` is not passed on for this variant; a GPU
is asked for from Python, `Sandbox.create(variant="coreweave", gpu="H100")`.
docs/docs/comparison/index.mdx:27
set_timeout()is not implemented by CoreWeave or Cloudflare (the only definitions are E2B and Datalayer), so it cannot have the same meaning on “each backend.” Qualify this shared-vocabulary claim by capability; otherwise users will reasonably call a method these providers do not expose.
- **One vocabulary for the rest.** `sandbox.files`, `sandbox.commands`,
`create_context()`, `set_timeout()`, names and tags mean the same thing on
each backend and are mapped onto whatever it actually offers — a filesystem
docs/docs/sandboxes/coreweave.mdx:164
- The CoreWeave implementation only times out the caller's wait for a driver reply; it does not interrupt the persistent driver, so code can continue running and mutate the namespace after this timeout. This statement promises stronger cancellation than the backend currently provides.
- There is no interrupt: `sandbox.interrupt()` answers `False`, and a runaway
execution is stopped by its timeout.
tests/test_cloudflare.py:130
- The bridge fake sends the same literal
******authorization value as the implementation and never validates it, so these protocol tests would pass even when the real bearer token is omitted or wrong. Make the fake requireBearer cf_keyand assert the header so the authentication path is covered.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
No description provided.