diff --git a/README.md b/README.md index bd5bf13..e1c5014 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 @@ -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: @@ -90,15 +92,6 @@ export 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: @@ -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 @@ -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 diff --git a/code_sandboxes/__init__.py b/code_sandboxes/__init__.py index 550fcf5..b31dac3 100644 --- a/code_sandboxes/__init__.py +++ b/code_sandboxes/__init__.py @@ -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: @@ -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 @@ -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 @@ -120,8 +123,6 @@ "CodeError", "CodeExecutionOutcome", "CodeSandboxClient", - "ColabKernelClient", - "ColabSandbox", "CommandResult", "Context", "ContextNotFoundError", @@ -135,6 +136,8 @@ "FileWatchEvent", "FileWatchEventType", "GPUType", + "GoogleColabKernelClient", + "GoogleColabSandbox", "ISandboxClient", "JupyterSandbox", "KaggleExecutionResult", @@ -177,6 +180,6 @@ "TunnelInfo", "VariableNotFoundError", "execution_result_to_reply", - "parse_colab_channels_url", + "parse_google_colab_channels_url", "parse_kaggle_channels_url", ] diff --git a/code_sandboxes/__version__.py b/code_sandboxes/__version__.py index 5145413..711c2f6 100644 --- a/code_sandboxes/__version__.py +++ b/code_sandboxes/__version__.py @@ -3,4 +3,4 @@ """Code Sandboxes.""" -__version__ = "1.0.2" +__version__ = "1.0.3" diff --git a/code_sandboxes/base.py b/code_sandboxes/base.py index f589fc5..a7c93d3 100644 --- a/code_sandboxes/base.py +++ b/code_sandboxes/base.py @@ -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 @@ -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 diff --git a/code_sandboxes/cli.py b/code_sandboxes/cli.py index 015d7f6..3440887 100644 --- a/code_sandboxes/cli.py +++ b/code_sandboxes/cli.py @@ -21,7 +21,8 @@ "docker", "eval", "monty", - "colab", + "google_colab", + "google-colab", "kaggle", "modal", "datalayer", @@ -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 @@ -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( @@ -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( diff --git a/code_sandboxes/colab.py b/code_sandboxes/google_colab.py similarity index 90% rename from code_sandboxes/colab.py rename to code_sandboxes/google_colab.py index 35f86e5..54ddd65 100644 --- a/code_sandboxes/colab.py +++ b/code_sandboxes/google_colab.py @@ -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. @@ -28,13 +28,13 @@ wss:///api/kernels//channels?session_id=<...>&colab-runtime-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://", ... kernel_id="", ... proxy_token="", @@ -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 @@ -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 @@ -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) diff --git a/code_sandboxes/colab_sandbox.py b/code_sandboxes/google_colab_sandbox.py similarity index 92% rename from code_sandboxes/colab_sandbox.py rename to code_sandboxes/google_colab_sandbox.py index f9bb934..9b58f6b 100644 --- a/code_sandboxes/colab_sandbox.py +++ b/code_sandboxes/google_colab_sandbox.py @@ -5,7 +5,7 @@ """Google Colab sandbox implementation. This sandbox connects to an existing Google Colab runtime and executes code in -its kernel using :class:`code_sandboxes.colab.ColabKernelClient`. +its kernel using :class:`code_sandboxes.google_colab.GoogleColabKernelClient`. Unlike the Jupyter/Docker sandboxes, this sandbox does **not** provision a runtime: a Colab runtime must already be running in a browser session. Reuse it @@ -20,8 +20,8 @@ import uuid from .base import Sandbox -from .colab import ColabKernelClient, parse_colab_channels_url from .exceptions import SandboxConfigurationError, SandboxNotStartedError +from .google_colab import GoogleColabKernelClient, parse_google_colab_channels_url from .interfaces import ISandboxClient from .models import ( CodeError, @@ -40,7 +40,7 @@ logger = logging.getLogger(__name__) -class ColabSandbox(Sandbox): +class GoogleColabSandbox(Sandbox): """Sandbox backed by a Google Colab runtime. Args: @@ -79,13 +79,13 @@ def __init__( def list_environments(cls) -> list[SandboxEnvironment]: return [ SandboxEnvironment( - name="colab", + name="google_colab", title="Google Colab", language="python", owner="google", visibility="cloud", burning_rate=0.0, - metadata={"variant": "colab"}, + metadata={"variant": "google_colab"}, ) ] @@ -96,20 +96,22 @@ def start(self) -> None: if self._channels_url and ( not self._server_url or not self._kernel_id or not self._proxy_token ): - parsed_server_url, parsed_kernel_id, parsed_proxy_token = parse_colab_channels_url( - self._channels_url - ) + ( + parsed_server_url, + parsed_kernel_id, + parsed_proxy_token, + ) = parse_google_colab_channels_url(self._channels_url) self._server_url = self._server_url or parsed_server_url self._kernel_id = self._kernel_id or parsed_kernel_id self._proxy_token = self._proxy_token or parsed_proxy_token if not self._server_url or not self._kernel_id or not self._proxy_token: raise SandboxConfigurationError( - "ColabSandbox requires 'server_url', 'kernel_id', and 'proxy_token'. " + "GoogleColabSandbox requires 'server_url', 'kernel_id', and 'proxy_token'. " "Provide them directly, or pass 'channels_url' from an active Colab session." ) - self._client = ColabKernelClient( + self._client = GoogleColabKernelClient( server_url=self._server_url, kernel_id=self._kernel_id, proxy_token=self._proxy_token, @@ -120,7 +122,7 @@ def start(self) -> None: self._default_context = self.create_context("default") self._info = SandboxInfo( id=self._sandbox_id, - variant="colab", + variant="google_colab", status=SandboxStatus.RUNNING, created_at=time.time(), name=self.config.name, @@ -168,7 +170,7 @@ def run_code( # noqa: C901 raise SandboxNotStartedError() if language != "python": - raise ValueError(f"ColabSandbox only supports Python, got: {language}") + raise ValueError(f"GoogleColabSandbox only supports Python, got: {language}") started_at = time.time() self._interrupt_requested.clear() diff --git a/code_sandboxes/models.py b/code_sandboxes/models.py index 88c57d7..4fd3984 100644 --- a/code_sandboxes/models.py +++ b/code_sandboxes/models.py @@ -69,7 +69,7 @@ class SandboxVariant(str, Enum): DOCKER = "docker" JUPYTER = "jupyter" DATALAYER = "datalayer" - COLAB = "colab" + GOOGLE_COLAB = "google_colab" KAGGLE = "kaggle" MONTY = "monty" MODAL = "modal" diff --git a/docs/docs/api-reference/index.mdx b/docs/docs/api-reference/index.mdx index 9091ee9..34bbd1e 100644 --- a/docs/docs/api-reference/index.mdx +++ b/docs/docs/api-reference/index.mdx @@ -36,7 +36,7 @@ def create( | Parameter | Type | Description | |-----------|------|-------------| -| `variant` | `str` | Sandbox type: `"eval"`, `"docker"`, `"jupyter"`, `"monty"`, `"kaggle"`, `"colab"`, `"modal"`, or `"datalayer"`. Defaults to `"datalayer"`. | +| `variant` | `str` | Sandbox type: `"eval"`, `"docker"`, `"jupyter"`, `"monty"`, `"kaggle"`, `"google_colab"`, `"modal"`, or `"datalayer"`. Defaults to `"datalayer"`. | | `timeout` | `float` | Execution timeout in seconds | | `environment` | `str` | Runtime environment name | | `gpu` | `str` | GPU type (e.g., `"T4"`, `"A100"`, `"H100"`) | @@ -82,7 +82,7 @@ def list_environments( | Parameter | Type | Description | |-----------|------|-------------| -| `variant` | `str` | Sandbox type: `"eval"`, `"docker"`, `"jupyter"`, `"monty"`, `"kaggle"`, `"colab"`, `"modal"`, or `"datalayer"` | +| `variant` | `str` | Sandbox type: `"eval"`, `"docker"`, `"jupyter"`, `"monty"`, `"kaggle"`, `"google_colab"`, `"modal"`, or `"datalayer"` | | `**kwargs` | `dict` | Variant-specific arguments (e.g., credentials, run URL) | Legacy `local-eval`, `local-docker`, and `local-jupyter` variant names are not supported. @@ -293,11 +293,11 @@ variants. They are exported from `code_sandboxes`: ```python from code_sandboxes import ( - ColabKernelClient, + GoogleColabKernelClient, KaggleExecutionResult, KaggleKernelClient, KaggleKernelExecutor, - parse_colab_channels_url, + parse_google_colab_channels_url, parse_kaggle_channels_url, ) ``` @@ -305,7 +305,7 @@ from code_sandboxes import ( - `KaggleKernelClient` connects to an interactive Kaggle notebook kernel. - `KaggleKernelExecutor` submits and monitors Kaggle batch notebook jobs. - `KaggleExecutionResult` normalizes batch output into a Jupyter-like reply. -- `ColabKernelClient` connects to an already-running Google Colab kernel. +- `GoogleColabKernelClient` connects to an already-running Google Colab kernel. - The parser helpers extract connection details from browser channels URLs. See [Kaggle](/sandboxes/kaggle) and [Google Colab](/sandboxes/google-colab) for diff --git a/docs/docs/cli/index.mdx b/docs/docs/cli/index.mdx index 6ec36b5..8924e69 100644 --- a/docs/docs/cli/index.mdx +++ b/docs/docs/cli/index.mdx @@ -29,7 +29,7 @@ Supported variants: - `eval` - `monty` - `kaggle` -- `colab` +- `google-colab` - `modal` - `datalayer` @@ -39,7 +39,7 @@ Supported variants: - `kaggle`: supports either interactive runtime settings or credential-based batch execution. - `monty`: starts a Monty REPL-backed sandbox. - `modal`: starts a Modal sandbox container. -- `colab`: prompts for runtime URL, kernel ID, and proxy token. +- `google-colab`: prompts for runtime URL, kernel ID, and proxy token. ## Usage diff --git a/docs/docs/comparison/index.mdx b/docs/docs/comparison/index.mdx index e399ea9..401c7bb 100644 --- a/docs/docs/comparison/index.mdx +++ b/docs/docs/comparison/index.mdx @@ -110,7 +110,7 @@ Modal is a serverless platform for running Python code in the cloud. It's design ### Code Sandboxes -Code Sandboxes provides a unified API across all supported variants (`eval`, `monty`, `docker`, `jupyter`, `kaggle`, `colab`, `modal`, `datalayer`), with native Jupyter kernel support. +Code Sandboxes provides a unified API across all supported variants (`eval`, `monty`, `docker`, `jupyter`, `kaggle`, `google-colab`, `modal`, `datalayer`), with native Jupyter kernel support. **Pros:** - Open source and self-hostable diff --git a/docs/docs/examples/index.mdx b/docs/docs/examples/index.mdx index 9d7dd79..b55750e 100644 --- a/docs/docs/examples/index.mdx +++ b/docs/docs/examples/index.mdx @@ -53,10 +53,10 @@ make kaggle ## Colab -- Source: https://github.com/datalayer/code-sandboxes/blob/main/examples/exec/colab_sandbox_example.py +- Source: https://github.com/datalayer/code-sandboxes/blob/main/examples/exec/google_colab_sandbox_example.py ```bash -make colab +make google-colab ``` ## Modal diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index 3520cd8..2a612ea 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -68,7 +68,7 @@ Code Sandboxes supports these execution variants: | `docker` | Container | Isolated execution | | `jupyter` | Process (Jupyter kernel) | Persistent notebook-style state | | `kaggle` | Managed notebook runtime | Interactive and batch runs | -| `colab` | Managed notebook runtime | Interactive Colab-connected runs | +| `google-colab` | Managed notebook runtime | Interactive Colab-connected runs | | `modal` | Managed container runtime | Ephemeral compute tasks | | `datalayer` | Managed VM/runtime | Production and GPU workloads | diff --git a/docs/docs/sandboxes/google-colab.mdx b/docs/docs/sandboxes/google-colab.mdx index 3caf0fc..d859465 100644 --- a/docs/docs/sandboxes/google-colab.mdx +++ b/docs/docs/sandboxes/google-colab.mdx @@ -6,7 +6,7 @@ title: Google Colab # Google Colab Runs code against a Google Colab runtime. Colab exposes a Jupyter-compatible -kernel behind an authenticating proxy, using the `ColabKernelClient` implemented +kernel behind an authenticating proxy, using the `GoogleColabKernelClient` implemented by Code Sandboxes. - **Requirements:** the base `code-sandboxes` installation. @@ -25,7 +25,7 @@ after the runtime is reassigned or reconnected. from code_sandboxes import Sandbox with Sandbox.create( - variant="colab", + variant="google_colab", server_url="https://8080-m-s-kkb-...-d.us-east1-0.prod.colab.dev", kernel_id="c9bba548-3995-4f26-8e1a-7b8fbb10c578", proxy_token="eyJhbGci....", @@ -39,7 +39,7 @@ Or pass a channels URL directly: ```python with Sandbox.create( - variant="colab", + variant="google_colab", channels_url=( "wss:///api/kernels//channels" "?session_id=<...>&colab-runtime-proxy-token=&colab-client-agent=web" @@ -50,7 +50,7 @@ with Sandbox.create( ## Kernel Client -Use `ColabKernelClient` to connect to a kernel that is already running in Colab. +Use `GoogleColabKernelClient` to connect to a kernel that is already running in Colab. This client reuses an existing Colab runtime; it does not create a Colab runtime from scratch. @@ -66,9 +66,9 @@ All three are available in Colab's channels WebSocket URL. ## Option A: Connect With Explicit Values ```python -from code_sandboxes import ColabKernelClient +from code_sandboxes import GoogleColabKernelClient -kernel = ColabKernelClient( +kernel = GoogleColabKernelClient( server_url="https://", kernel_id="", proxy_token="", @@ -83,14 +83,14 @@ kernel.stop(shutdown_kernel=False) ## Option B: Connect From Channels URL ```python -from code_sandboxes import ColabKernelClient +from code_sandboxes import GoogleColabKernelClient channels_url = ( "wss:///api/kernels//channels" "?session_id=<...>&colab-runtime-proxy-token=&colab-client-agent=web" ) -with ColabKernelClient.from_channels_url(channels_url) as kernel: +with GoogleColabKernelClient.from_channels_url(channels_url) as kernel: reply = kernel.execute("x = 1 + 1; print(x)") print(reply) ``` @@ -98,12 +98,12 @@ with ColabKernelClient.from_channels_url(channels_url) as kernel: You can also parse values directly: ```python -from code_sandboxes import parse_colab_channels_url +from code_sandboxes import parse_google_colab_channels_url -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) ``` -`ColabKernelClient` forwards the proxy token as both the +`GoogleColabKernelClient` forwards the proxy token as both the `X-Colab-Runtime-Proxy-Token` HTTP header and the `colab-runtime-proxy-token` WebSocket query parameter. diff --git a/docs/docs/sandboxes/index.mdx b/docs/docs/sandboxes/index.mdx index b8e9352..8c0c2d1 100644 --- a/docs/docs/sandboxes/index.mdx +++ b/docs/docs/sandboxes/index.mdx @@ -12,7 +12,9 @@ A sandbox is an isolated environment where code can be executed safely. Code San Use `Sandbox.create()` to create a new sandbox: Canonical variant names are `jupyter`, `docker`, `eval`, `monty`, `kaggle`, -`colab`, `modal`, and `datalayer`. Older `local-*` names are no longer supported. +`google_colab`, `modal`, and `datalayer`. Older `local-*` names are no longer supported. + +The CLI also accepts `google-colab` as an alias for `google_colab`. ```python from code_sandboxes import Sandbox @@ -65,7 +67,7 @@ Each page below explains how to configure each variant. | [`monty`](./monty) | Secure in-process Python subset via Monty | | [`docker`](./docker) | Jupyter execution in a Docker container | | [`kaggle`](./kaggle) | Kaggle runtime (interactive or batch) | -| [`colab`](./google-colab) | Google Colab runtime via runtime proxy | +| [`google_colab`](./google-colab) | Google Colab runtime via runtime proxy | | [`modal`](./modal) | Modal container execution | | [`datalayer`](./datalayer) | Datalayer managed runtime with optional GPU | diff --git a/examples/README.md b/examples/README.md index 0d68452..25be16f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -21,7 +21,7 @@ Supported sandbox variants: - `docker` - `eval` - `monty` -- `colab` +- `google-colab` - `kaggle` - `modal` - `datalayer` @@ -34,7 +34,7 @@ python eval_sandbox_example.py python jupyter_sandbox_example.py python docker_sandbox_example.py python monty_sandbox_example.py -python colab_sandbox_example.py +python google_colab_sandbox_example.py python kaggle_sandbox_example.py python modal_sandbox_example.py python datalayer_sandbox_example.py @@ -48,7 +48,7 @@ make eval make jupyter make docker make monty -make colab +make google-colab make kaggle make modal make datalayer @@ -62,7 +62,7 @@ make eval make jupyter make docker make monty -make colab +make google-colab make kaggle make modal make datalayer @@ -72,7 +72,7 @@ Notes by variant: - `docker`: requires Docker support and a Docker image (for example `code-sandboxes-jupyter:latest`). - `monty`: requires `code-sandboxes[monty]` (`pydantic-monty`). -- `colab`: requires `RUNTIME_URL`, `RUNTIME_ID`, and `RUNTIME_PROXY_TOKEN`. +- `google-colab`: requires `RUNTIME_URL`, `RUNTIME_ID`, and `RUNTIME_PROXY_TOKEN`. - `kaggle`: requires `RUNTIME_CHANNELS_URL`, or `RUNTIME_URL` and `RUNTIME_ID`. - `modal`: requires `MODAL_TOKEN_ID`/`MODAL_TOKEN_SECRET` or `~/.modal.toml`. - `datalayer`: requires Datalayer runtime credentials/config. diff --git a/examples/exec/Makefile b/examples/exec/Makefile index cd77d32..f18c1c8 100644 --- a/examples/exec/Makefile +++ b/examples/exec/Makefile @@ -2,9 +2,9 @@ PYTHON ?= python -.PHONY: all eval docker jupyter monty colab kaggle modal modal-gpu datalayer +.PHONY: all eval docker jupyter monty google-colab kaggle modal modal-gpu datalayer -all: eval docker jupyter monty colab modal datalayer +all: eval docker jupyter monty google-colab modal datalayer eval: $(PYTHON) eval_sandbox_example.py @@ -18,8 +18,8 @@ jupyter: monty: $(PYTHON) monty_sandbox_example.py -colab: - $(PYTHON) colab_sandbox_example.py +google-colab: + $(PYTHON) google_colab_sandbox_example.py kaggle: $(PYTHON) kaggle_sandbox_example.py diff --git a/examples/exec/colab_sandbox_example.py b/examples/exec/google_colab_sandbox_example.py similarity index 97% rename from examples/exec/colab_sandbox_example.py rename to examples/exec/google_colab_sandbox_example.py index 5c768ab..ceeeba9 100644 --- a/examples/exec/colab_sandbox_example.py +++ b/examples/exec/google_colab_sandbox_example.py @@ -29,7 +29,7 @@ def main() -> None: runtime_proxy_token = _require("RUNTIME_PROXY_TOKEN") with Sandbox.create( - variant="colab", + variant="google_colab", timeout=60, server_url=runtime_url, kernel_id=runtime_id, diff --git a/examples/repl/Makefile b/examples/repl/Makefile index 596d718..7afc3b8 100644 --- a/examples/repl/Makefile +++ b/examples/repl/Makefile @@ -2,9 +2,9 @@ PYTHON ?= python -.PHONY: all eval docker jupyter monty colab kaggle modal modal-gpu datalayer +.PHONY: all eval docker jupyter monty google-colab kaggle modal modal-gpu datalayer -all: eval docker jupyter monty colab modal datalayer +all: eval docker jupyter monty google-colab modal datalayer eval: $(PYTHON) eval_sandbox_example.py @@ -18,8 +18,8 @@ jupyter: monty: $(PYTHON) monty_sandbox_example.py -colab: - $(PYTHON) colab_sandbox_example.py +google-colab: + $(PYTHON) google_colab_sandbox_example.py kaggle: $(PYTHON) kaggle_sandbox_example.py diff --git a/examples/repl/colab_sandbox_example.py b/examples/repl/google_colab_sandbox_example.py similarity index 96% rename from examples/repl/colab_sandbox_example.py rename to examples/repl/google_colab_sandbox_example.py index cf534b5..59bf7c4 100644 --- a/examples/repl/colab_sandbox_example.py +++ b/examples/repl/google_colab_sandbox_example.py @@ -24,7 +24,7 @@ def main() -> None: runtime_proxy_token = _require("RUNTIME_PROXY_TOKEN") with Sandbox.create( - variant="colab", + variant="google_colab", timeout=60, server_url=runtime_url, kernel_id=runtime_id, diff --git a/pyproject.toml b/pyproject.toml index 6922b7e..9766d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ code-sandboxes = "code_sandboxes.cli:main" [project.optional-dependencies] datalayer = ["agent_runtimes>=1.0.16"] docker = ["docker>=6.0"] +google-colab = [] kaggle = ["kaggle>=1.6"] monty = ["pydantic-monty"] modal = ["modal>=0.64"] diff --git a/tests/test_cli_repl.py b/tests/test_cli_repl.py index d21d531..31fe261 100644 --- a/tests/test_cli_repl.py +++ b/tests/test_cli_repl.py @@ -63,10 +63,14 @@ def _fake_create(*args, **kwargs): # Prompts: server_url, kernel_id, proxy_token, then repl command. user_input = "https://colab-host.example\nkernel-abc\nproxy-xyz\n:exit\n" - result = runner.invoke(sandbox_cli.app, ["repl", "--variant", "colab"], input=user_input) + result = runner.invoke( + sandbox_cli.app, + ["repl", "--variant", "google-colab"], + input=user_input, + ) assert result.exit_code == 0 - assert captured["kwargs"]["variant"] == "colab" + assert captured["kwargs"]["variant"] == "google_colab" assert captured["kwargs"]["server_url"] == "https://colab-host.example" assert captured["kwargs"]["kernel_id"] == "kernel-abc" assert captured["kwargs"]["proxy_token"] == "proxy-xyz" # noqa: S105 diff --git a/tests/test_factory.py b/tests/test_factory.py index 5130856..aaca4a0 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -7,10 +7,10 @@ import pytest from code_sandboxes.base import Sandbox, SandboxVariant -from code_sandboxes.colab_sandbox import ColabSandbox from code_sandboxes.datalayer_sandbox import DatalayerSandbox from code_sandboxes.docker_sandbox import DockerSandbox from code_sandboxes.eval_sandbox import EvalSandbox +from code_sandboxes.google_colab_sandbox import GoogleColabSandbox from code_sandboxes.jupyter_sandbox import JupyterSandbox from code_sandboxes.kaggle_sandbox import KaggleSandbox from code_sandboxes.modal_sandbox import ModalSandbox @@ -70,7 +70,7 @@ def test_create_invalid_variant(self): ("jupyter", JupyterSandbox), ("docker", DockerSandbox), ("datalayer", DatalayerSandbox), - ("colab", ColabSandbox), + ("google_colab", GoogleColabSandbox), ("kaggle", KaggleSandbox), ("monty", MontySandbox), ("modal", ModalSandbox), @@ -89,7 +89,7 @@ def test_create_default_variant_is_datalayer(self): def test_create_colab_forwards_connection_kwargs(self): """Test that Colab-specific connection kwargs are propagated.""" sandbox = Sandbox.create( - variant="colab", + variant="google_colab", server_url="https://colab-host.example", kernel_id="kernel-id", proxy_token="proxy-token", # noqa: S106 @@ -99,7 +99,7 @@ def test_create_colab_forwards_connection_kwargs(self): ), client_agent="agent-name", ) - assert isinstance(sandbox, ColabSandbox) + assert isinstance(sandbox, GoogleColabSandbox) assert sandbox._server_url == "https://colab-host.example" assert sandbox._kernel_id == "kernel-id" assert sandbox._proxy_token == "proxy-token" # noqa: S105 diff --git a/tests/test_colab.py b/tests/test_google_colab.py similarity index 79% rename from tests/test_colab.py rename to tests/test_google_colab.py index 5604a99..1ee11b2 100644 --- a/tests/test_colab.py +++ b/tests/test_google_colab.py @@ -8,12 +8,12 @@ import pytest -from code_sandboxes.colab import ( +from code_sandboxes.google_colab import ( COLAB_CLIENT_AGENT_HEADER, COLAB_RUNTIME_PROXY_TOKEN_HEADER, COLAB_RUNTIME_PROXY_TOKEN_PARAM, - ColabKernelClient, - parse_colab_channels_url, + GoogleColabKernelClient, + parse_google_colab_channels_url, ) CHANNELS_URL = ( @@ -35,10 +35,10 @@ def fake_kernel_client_init(self, *args, **kwargs): captured.update(kwargs) monkeypatch.setattr( - "code_sandboxes.colab.JupyterKernelClient.__init__", fake_kernel_client_init + "code_sandboxes.google_colab.JupyterKernelClient.__init__", fake_kernel_client_init ) - ColabKernelClient( + GoogleColabKernelClient( server_url="https://colab-host.example", kernel_id="kernel-123", proxy_token="proxy-abc", # noqa: S106 @@ -68,10 +68,10 @@ def fake_kernel_client_init(self, *args, **kwargs): captured.update(kwargs) monkeypatch.setattr( - "code_sandboxes.colab.JupyterKernelClient.__init__", fake_kernel_client_init + "code_sandboxes.google_colab.JupyterKernelClient.__init__", fake_kernel_client_init ) - ColabKernelClient( + GoogleColabKernelClient( server_url="https://colab-host.example", kernel_id="kernel-123", proxy_token="proxy-abc", # noqa: S106 @@ -83,26 +83,26 @@ def fake_kernel_client_init(self, *args, **kwargs): def test_parse_colab_channels_url_extracts_parts(): - 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) assert server_url == SERVER_URL assert kernel_id == KERNEL_ID assert proxy_token == PROXY_TOKEN def test_parse_colab_channels_url_maps_ws_to_http(): - server_url, _, _ = parse_colab_channels_url(CHANNELS_URL.replace("wss://", "ws://")) + server_url, _, _ = parse_google_colab_channels_url(CHANNELS_URL.replace("wss://", "ws://")) assert server_url.startswith("http://") def test_parse_colab_channels_url_requires_proxy_token(): without_token = CHANNELS_URL.replace("&colab-runtime-proxy-token=proxy-abc", "") with pytest.raises(ValueError): - parse_colab_channels_url(without_token) + parse_google_colab_channels_url(without_token) def test_parse_colab_channels_url_rejects_invalid_url(): with pytest.raises(ValueError): - parse_colab_channels_url("https://colab.research.google.com/not-a-channels-url") + parse_google_colab_channels_url("https://colab.research.google.com/not-a-channels-url") def test_colab_kernel_client_from_channels_url(monkeypatch): @@ -112,10 +112,10 @@ def fake_kernel_client_init(self, *args, **kwargs): captured.update(kwargs) monkeypatch.setattr( - "code_sandboxes.colab.JupyterKernelClient.__init__", fake_kernel_client_init + "code_sandboxes.google_colab.JupyterKernelClient.__init__", fake_kernel_client_init ) - ColabKernelClient.from_channels_url(CHANNELS_URL) + GoogleColabKernelClient.from_channels_url(CHANNELS_URL) assert captured["server_url"] == SERVER_URL assert captured["kernel_id"] == KERNEL_ID diff --git a/tests/test_modal_colab_sandbox.py b/tests/test_modal_google_colab_sandbox.py similarity index 99% rename from tests/test_modal_colab_sandbox.py rename to tests/test_modal_google_colab_sandbox.py index 06e9ee6..bc7f09b 100644 --- a/tests/test_modal_colab_sandbox.py +++ b/tests/test_modal_google_colab_sandbox.py @@ -12,7 +12,7 @@ import pytest -from code_sandboxes.colab_sandbox import ColabSandbox +from code_sandboxes.google_colab_sandbox import GoogleColabSandbox from code_sandboxes.kaggle_sandbox import KaggleSandbox from code_sandboxes.modal_sandbox import ModalSandbox from code_sandboxes.models import SandboxConfig @@ -89,7 +89,7 @@ def test_modal_nonzero_return_without_stderr_sets_exit_code(): def test_colab_execute_exception_sets_execution_ok_false(): """Infrastructure execute errors must set execution_ok to False.""" - sandbox = ColabSandbox( + sandbox = GoogleColabSandbox( config=SandboxConfig(timeout=10.0), server_url="https://colab-host.example", kernel_id="kernel-id", diff --git a/tests/test_models.py b/tests/test_models.py index a2ddf25..8c88372 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -45,7 +45,7 @@ def test_sandbox_variant_enum(self): assert SandboxVariantEnum.DOCKER.value == "docker" assert SandboxVariantEnum.JUPYTER.value == "jupyter" assert SandboxVariantEnum.DATALAYER.value == "datalayer" - assert SandboxVariantEnum.COLAB.value == "colab" + assert SandboxVariantEnum.GOOGLE_COLAB.value == "google_colab" assert SandboxVariantEnum.KAGGLE.value == "kaggle" assert SandboxVariantEnum.MONTY.value == "monty" assert SandboxVariantEnum.MODAL.value == "modal"