Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 20 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@
[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io)

[![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
[![PyPI - Version](https://img.shields.io/pypi/v/code-sandboxes)](https://pypi.org/project/code-sandboxes)

# { } 📦 Code Sandboxes

[![PyPI - Version](https://img.shields.io/pypi/v/code-sandboxes)](https://pypi.org/project/code-sandboxes)

Code Sandboxes (`code_sandboxes`) is a Python package for running code in isolated sandbox variants through a unified API.

Canonical variant names:

- `jupyter`
- `datalayer`
- `docker`
- `eval`
- `monty`
- `google_colab`
- `jupyter`
- `kaggle`
- `colab`
- `modal`
- `datalayer`
- `monty`

## Documentation

Expand All @@ -49,9 +48,7 @@ pip install code-sandboxes

For backend-specific extras and credentials, see [https://code-sandboxes.datalayer.tech/installation](https://code-sandboxes.datalayer.tech/installation) and [https://code-sandboxes.datalayer.tech/sandboxes](https://code-sandboxes.datalayer.tech/sandboxes).

## Quick Examples

### Python: launch a `jupyter` sandbox
### Jupyter Sandbox

```python
from code_sandboxes import Sandbox
Expand All @@ -70,9 +67,14 @@ with Sandbox.create(
print(sandbox.run_code("x + 2").text) # 42
```

### CLI REPL: `kaggle` variant
## Kaggle Sandbox

Kaggle supports both batch execution and interactive connections through the
`kaggle` sandbox. Install its optional dependency first:

Kaggle REPL supports both interactive runtime mode and credential-based batch mode.
```bash
pip install "code-sandboxes[kaggle]"
```

Required credentials for batch mode:

Expand All @@ -90,15 +92,6 @@ export KAGGLE_API_KEY="<your-kaggle-api-key>"
sandbox repl --variant kaggle
```

### Kaggle

Kaggle supports both batch execution and interactive connections through the
`kaggle` sandbox. Install its optional dependency first:

```bash
pip install "code-sandboxes[kaggle]"
```

For batch execution, configure Kaggle credentials and create the sandbox
without a runtime URL:

Expand Down Expand Up @@ -136,10 +129,10 @@ with KaggleKernelClient.from_channels_url(channels_url, token=None) as kernel:
print(kernel.execute("x = 1 + 1; print(x)"))
```

See the [complete Kaggle guide](docs/docs/sandboxes/kaggle.mdx) for authentication,
See the [complete Kaggle guide](https://code-sandboxes.datalayer.tech/sandboxes/kaggle) for authentication,
accelerators, channels URL retrieval, and execution options.

### Google Colab
## Google Colab

Google Colab exposes an already-running kernel through an authenticating proxy.
Copy its WebSocket channels URL from the browser's Network tools, then pass it
Expand All @@ -148,29 +141,23 @@ directly to the sandbox:
```python
from code_sandboxes import Sandbox

with Sandbox.create(variant="colab", channels_url=channels_url) as sandbox:
with Sandbox.create(variant="google_colab", channels_url=channels_url) as sandbox:
print(sandbox.run_code("x = 1 + 1; print(x)").stdout)
```

The lower-level client and parser are owned by Code Sandboxes as well:

```python
from code_sandboxes import ColabKernelClient, parse_colab_channels_url
from code_sandboxes import GoogleColabKernelClient, parse_google_colab_channels_url

server_url, kernel_id, proxy_token = parse_colab_channels_url(channels_url)
with ColabKernelClient.from_channels_url(channels_url) as kernel:
server_url, kernel_id, proxy_token = parse_google_colab_channels_url(channels_url)
with GoogleColabKernelClient.from_channels_url(channels_url) as kernel:
print(kernel.execute("print('hello from colab')"))
```

See the [complete Google Colab guide](docs/docs/sandboxes/google-colab.mdx) for
See the [complete Google Colab guide](https://code-sandboxes.datalayer.tech/sandboxes/google-colab) for
proxy authentication, explicit connection values, and channels URL retrieval.

For full setup and parameters for all variants, see:

- [https://code-sandboxes.datalayer.tech/sandboxes](https://code-sandboxes.datalayer.tech/sandboxes)
- [https://code-sandboxes.datalayer.tech/cli](https://code-sandboxes.datalayer.tech/cli)
- [https://code-sandboxes.datalayer.tech/installation](https://code-sandboxes.datalayer.tech/installation)

## License

BSD 3-Clause License
15 changes: 9 additions & 6 deletions code_sandboxes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- DockerSandbox: Docker container based, good isolation
- JupyterSandbox: Jupyter Server with persistent kernel state
- DatalayerSandbox: Cloud-based Datalayer runtime, full isolation
- ColabSandbox: Google Colab runtime, connects to an assigned kernel
- GoogleColabSandbox: Google Colab runtime, connects to an assigned kernel
- KaggleSandbox: Kaggle runtime, connects to an interactive notebook kernel

Cloud container sandboxes:
Expand Down Expand Up @@ -59,8 +59,6 @@

from .base import Sandbox
from .client import CodeExecutionOutcome, CodeSandboxClient, execution_result_to_reply
from .colab import ColabKernelClient, parse_colab_channels_url
from .colab_sandbox import ColabSandbox
from .commands import CommandResult, ProcessHandle, SandboxCommands
from .datalayer_sandbox import DatalayerSandbox
from .docker_sandbox import DockerSandbox
Expand All @@ -87,6 +85,11 @@
SandboxFileHandle,
SandboxFilesystem,
)
from .google_colab import (
GoogleColabKernelClient,
parse_google_colab_channels_url,
)
from .google_colab_sandbox import GoogleColabSandbox
from .interfaces import ISandboxClient
from .jupyter_sandbox import JupyterSandbox
from .kaggle import KAGGLE_API_TOKEN_ENV, KaggleKernelClient, parse_kaggle_channels_url
Expand Down Expand Up @@ -120,8 +123,6 @@
"CodeError",
"CodeExecutionOutcome",
"CodeSandboxClient",
"ColabKernelClient",
"ColabSandbox",
"CommandResult",
"Context",
"ContextNotFoundError",
Expand All @@ -135,6 +136,8 @@
"FileWatchEvent",
"FileWatchEventType",
"GPUType",
"GoogleColabKernelClient",
"GoogleColabSandbox",
"ISandboxClient",
"JupyterSandbox",
"KaggleExecutionResult",
Expand Down Expand Up @@ -177,6 +180,6 @@
"TunnelInfo",
"VariableNotFoundError",
"execution_result_to_reply",
"parse_colab_channels_url",
"parse_google_colab_channels_url",
"parse_kaggle_channels_url",
]
2 changes: 1 addition & 1 deletion code_sandboxes/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

"""Code Sandboxes."""

__version__ = "1.0.2"
__version__ = "1.0.3"
8 changes: 4 additions & 4 deletions code_sandboxes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ def create( # noqa: C901
from .datalayer_sandbox import DatalayerSandbox

sandbox = DatalayerSandbox(config=config, **kwargs)
elif variant_value == "colab":
from .colab_sandbox import ColabSandbox
elif variant_value == "google_colab":
from .google_colab_sandbox import GoogleColabSandbox

sandbox = ColabSandbox(config=config, **kwargs)
sandbox = GoogleColabSandbox(config=config, **kwargs)
elif variant_value == "kaggle":
from .kaggle_sandbox import KaggleSandbox

Expand All @@ -289,7 +289,7 @@ def create( # noqa: C901
raise ValueError(
f"Unknown sandbox variant: {variant}. "
"Supported variants: eval, docker, jupyter, "
"datalayer, colab, kaggle, monty, modal"
"datalayer, google_colab, kaggle, monty, modal"
)

# Set tags if provided
Expand Down
12 changes: 9 additions & 3 deletions code_sandboxes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"docker",
"eval",
"monty",
"colab",
"google_colab",
"google-colab",
"kaggle",
"modal",
"datalayer",
Expand Down Expand Up @@ -80,6 +81,8 @@ def _resolve_variant(variant: str | None) -> str:
f"Unsupported variant: {selected}. Supported values: "
+ ", ".join(sorted(_SUPPORTED_REPL_VARIANTS))
)
if selected == "google-colab":
return "google_colab"
return selected


Expand All @@ -98,7 +101,7 @@ def _resolve_variant_kwargs(
# Match `jupyter console` behavior by launching local Jupyter on random port.
kwargs["port"] = 0

if variant == "colab":
if variant == "google_colab":
kwargs["server_url"] = server_url or typer.prompt("Colab runtime URL (RUNTIME_URL)")
kwargs["kernel_id"] = kernel_id or typer.prompt("Colab kernel id (RUNTIME_ID)")
kwargs["proxy_token"] = proxy_token or typer.prompt(
Expand Down Expand Up @@ -208,7 +211,10 @@ def repl(
None,
"--variant",
"-v",
help="Sandbox variant (jupyter, docker, eval, monty, colab, kaggle, modal, datalayer).",
help=(
"Sandbox variant (jupyter, docker, eval, monty, "
"google_colab/google-colab, kaggle, modal, datalayer)."
),
),
timeout: float = typer.Option(60.0, help="Default code execution timeout (seconds)."),
environment: str | None = typer.Option(
Expand Down
24 changes: 12 additions & 12 deletions code_sandboxes/colab.py → code_sandboxes/google_colab.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"""Google Colab kernel client.

This module provides :class:`ColabKernelClient`, a thin specialization of
This module provides :class:`GoogleColabKernelClient`, a thin specialization of
:class:`~jupyter_kernel_client.client.JupyterKernelClient` that connects to an
**already-running** Google Colab kernel.

Expand All @@ -28,13 +28,13 @@

wss://<host>/api/kernels/<kernel_id>/channels?session_id=<...>&colab-runtime-proxy-token=<proxy_token>&colab-client-agent=web

Use :func:`parse_colab_channels_url` (or
:meth:`ColabKernelClient.from_channels_url`) to turn that URL into the
Use :func:`parse_google_colab_channels_url` (or
:meth:`GoogleColabKernelClient.from_channels_url`) to turn that URL into the
``server_url``, ``kernel_id`` and ``proxy_token`` expected by the client.

Example:
>>> from code_sandboxes import ColabKernelClient
>>> kernel = ColabKernelClient(
>>> from code_sandboxes import GoogleColabKernelClient
>>> kernel = GoogleColabKernelClient(
... server_url="https://<colab-host>",
... kernel_id="<kernel_id>",
... proxy_token="<proxy_token>",
Expand Down Expand Up @@ -68,7 +68,7 @@
_COLAB_KERNEL_RE = re.compile(r"/api/kernels/([^/]+)/channels", re.IGNORECASE)


def parse_colab_channels_url(channels_url: str) -> tuple[str, str, str]:
def parse_google_colab_channels_url(channels_url: str) -> tuple[str, str, str]:
"""Extract ``server_url``, ``kernel_id`` and ``proxy_token`` from a URL.

Parses the websocket *channels* URL of a running Colab kernel session, as
Expand Down Expand Up @@ -128,7 +128,7 @@ def parse_colab_channels_url(channels_url: str) -> tuple[str, str, str]:
return server_url, kernel_id, proxy_token


class ColabKernelClient(JupyterKernelClient):
class GoogleColabKernelClient(JupyterKernelClient):
"""Kernel client connected to an existing Google Colab runtime.

This client connects to a kernel that is **already running** on a Colab
Expand Down Expand Up @@ -190,19 +190,19 @@ def from_channels_url(
cls,
channels_url: str,
**kwargs: t.Any,
) -> ColabKernelClient:
) -> GoogleColabKernelClient:
"""Create a client from a Colab kernel session *channels* URL.

Args:
channels_url: The websocket *channels* URL of a running Colab kernel
session (see :func:`parse_colab_channels_url`).
**kwargs: Forwarded to :class:`ColabKernelClient`. Values provided
session (see :func:`parse_google_colab_channels_url`).
**kwargs: Forwarded to :class:`GoogleColabKernelClient`. Values provided
here override those parsed from the URL.

Returns:
A configured :class:`ColabKernelClient` instance.
A configured :class:`GoogleColabKernelClient` instance.
"""
server_url, kernel_id, proxy_token = parse_colab_channels_url(channels_url)
server_url, kernel_id, proxy_token = parse_google_colab_channels_url(channels_url)
kwargs.setdefault("kernel_id", kernel_id)
kwargs.setdefault("proxy_token", proxy_token)
return cls(server_url=server_url, **kwargs)
Loading