From 4f95bbb57b18763d2ff6c6ee41d2956007830e2f Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:52:26 +0200 Subject: [PATCH 1/3] Support E2B_SANDBOX_URL env var and sandbox URL option for Jupyter requests Resolve the Jupyter server URL through the sandbox URL override (sandboxUrl/sandbox_url option or E2B_SANDBOX_URL environment variable), matching the base E2B SDK, and send E2b-Sandbox-Id and E2b-Sandbox-Port headers on Jupyter requests so a gateway or proxy can route them. Co-Authored-By: Claude Fable 5 --- .changeset/great-moons-agree.md | 6 +++ js/src/sandbox.ts | 17 +++++-- js/tests/sandboxUrl.test.ts | 50 +++++++++++++++++++ .../code_interpreter_async.py | 15 ++++++ .../code_interpreter_sync.py | 35 +++++++++++-- python/tests/test_sandbox_url.py | 38 ++++++++++++++ 6 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 .changeset/great-moons-agree.md create mode 100644 js/tests/sandboxUrl.test.ts create mode 100644 python/tests/test_sandbox_url.py diff --git a/.changeset/great-moons-agree.md b/.changeset/great-moons-agree.md new file mode 100644 index 00000000..6f5397fd --- /dev/null +++ b/.changeset/great-moons-agree.md @@ -0,0 +1,6 @@ +--- +'@e2b/code-interpreter': minor +'@e2b/code-interpreter-python': minor +--- + +Honor the `sandboxUrl`/`sandbox_url` option and the `E2B_SANDBOX_URL` environment variable when connecting to the Jupyter server, matching the base E2B SDK. Jupyter requests now also send the `E2b-Sandbox-Id` and `E2b-Sandbox-Port` headers so a custom sandbox URL (e.g. a gateway or proxy) can route them to the right sandbox and port. diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index 14aef88e..00d60c84 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -138,9 +138,10 @@ export class Sandbox extends BaseSandbox { 'code-interpreter-v1' protected get jupyterUrl(): string { - return `${this.connectionConfig.debug ? 'http' : 'https'}://${this.getHost( - JUPYTER_PORT - )}` + return this.connectionConfig.getSandboxDirectUrl(this.sandboxId, { + sandboxDomain: this.sandboxDomain, + envdPort: JUPYTER_PORT, + }) } /** @@ -214,6 +215,8 @@ export class Sandbox extends BaseSandbox { const headers: Record = { 'Content-Type': 'application/json', + 'E2b-Sandbox-Id': this.sandboxId, + 'E2b-Sandbox-Port': JUPYTER_PORT.toString(), } if (this.trafficAccessToken) { @@ -294,6 +297,8 @@ export class Sandbox extends BaseSandbox { try { const headers: Record = { 'Content-Type': 'application/json', + 'E2b-Sandbox-Id': this.sandboxId, + 'E2b-Sandbox-Port': JUPYTER_PORT.toString(), } if (this.trafficAccessToken) { @@ -334,6 +339,8 @@ export class Sandbox extends BaseSandbox { const id = typeof context === 'string' ? context : context.id const headers: Record = { 'Content-Type': 'application/json', + 'E2b-Sandbox-Id': this.sandboxId, + 'E2b-Sandbox-Port': JUPYTER_PORT.toString(), } if (this.trafficAccessToken) { @@ -367,6 +374,8 @@ export class Sandbox extends BaseSandbox { try { const headers: Record = { 'Content-Type': 'application/json', + 'E2b-Sandbox-Id': this.sandboxId, + 'E2b-Sandbox-Port': JUPYTER_PORT.toString(), } if (this.trafficAccessToken) { @@ -405,6 +414,8 @@ export class Sandbox extends BaseSandbox { const id = typeof context === 'string' ? context : context.id const headers: Record = { 'Content-Type': 'application/json', + 'E2b-Sandbox-Id': this.sandboxId, + 'E2b-Sandbox-Port': JUPYTER_PORT.toString(), } if (this.trafficAccessToken) { diff --git a/js/tests/sandboxUrl.test.ts b/js/tests/sandboxUrl.test.ts new file mode 100644 index 00000000..4566f6cd --- /dev/null +++ b/js/tests/sandboxUrl.test.ts @@ -0,0 +1,50 @@ +import { afterEach, beforeEach, expect, test } from 'vitest' + +import { Sandbox } from '../src' + +// Constructing a sandbox instance makes no network requests, so URL +// resolution can be tested without a live sandbox. +function createSandbox(opts: object = {}) { + const SandboxClass = Sandbox as unknown as new (opts: object) => Sandbox + const sandbox = new SandboxClass({ + sandboxId: 'test-sandbox-id', + envdVersion: '0.2.0', + ...opts, + }) + return sandbox as unknown as { jupyterUrl: string } +} + +const savedEnv: Record = {} + +beforeEach(() => { + for (const key of ['E2B_SANDBOX_URL', 'E2B_DEBUG']) { + savedEnv[key] = process.env[key] + delete process.env[key] + } +}) + +afterEach(() => { + for (const [key, value] of Object.entries(savedEnv)) { + if (value === undefined) { + delete process.env[key] + } else { + process.env[key] = value + } + } +}) + +test('jupyterUrl points directly to the sandbox host by default', () => { + const sandbox = createSandbox({ domain: 'example.dev' }) + expect(sandbox.jupyterUrl).toBe('https://49999-test-sandbox-id.example.dev') +}) + +test('jupyterUrl honors the sandboxUrl option', () => { + const sandbox = createSandbox({ sandboxUrl: 'https://proxy.example.com' }) + expect(sandbox.jupyterUrl).toBe('https://proxy.example.com') +}) + +test('jupyterUrl honors the E2B_SANDBOX_URL environment variable', () => { + process.env.E2B_SANDBOX_URL = 'https://env.example.com' + const sandbox = createSandbox() + expect(sandbox.jupyterUrl).toBe('https://env.example.com') +}) diff --git a/python/e2b_code_interpreter/code_interpreter_async.py b/python/e2b_code_interpreter/code_interpreter_async.py index fcc2737a..82a58b51 100644 --- a/python/e2b_code_interpreter/code_interpreter_async.py +++ b/python/e2b_code_interpreter/code_interpreter_async.py @@ -61,6 +61,11 @@ class AsyncSandbox(BaseAsyncSandbox): @property def _jupyter_url(self) -> str: + # Honors the `sandbox_url` option and the `E2B_SANDBOX_URL` environment + # variable, same as the base SDK does for envd requests. + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property @@ -196,6 +201,8 @@ async def run_code( try: headers = { "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token @@ -265,6 +272,8 @@ async def create_code_context( try: headers = { "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), } if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -304,6 +313,8 @@ async def remove_code_context( try: headers = { "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), } if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -332,6 +343,8 @@ async def list_code_contexts(self) -> List[Context]: try: headers = { "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), } if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -369,6 +382,8 @@ async def restart_code_context( try: headers = { "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), } if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token diff --git a/python/e2b_code_interpreter/code_interpreter_sync.py b/python/e2b_code_interpreter/code_interpreter_sync.py index 35850e56..ddfe4aad 100644 --- a/python/e2b_code_interpreter/code_interpreter_sync.py +++ b/python/e2b_code_interpreter/code_interpreter_sync.py @@ -58,6 +58,11 @@ class Sandbox(BaseSandbox): @property def _jupyter_url(self) -> str: + # Honors the `sandbox_url` option and the `E2B_SANDBOX_URL` environment + # variable, same as the base SDK does for envd requests. + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property @@ -189,7 +194,11 @@ def run_code( context_id = context.id if context else None try: - headers: Dict[str, str] = {"Content-Type": "application/json"} + headers: Dict[str, str] = { + "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -256,7 +265,11 @@ def create_code_context( data["cwd"] = cwd try: - headers: Dict[str, str] = {"Content-Type": "application/json"} + headers: Dict[str, str] = { + "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -295,7 +308,11 @@ def remove_code_context( context_id = context.id if isinstance(context, Context) else context try: - headers: Dict[str, str] = {"Content-Type": "application/json"} + headers: Dict[str, str] = { + "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -323,7 +340,11 @@ def list_code_contexts(self) -> List[Context]: :return: List of contexts. """ try: - headers: Dict[str, str] = {"Content-Type": "application/json"} + headers: Dict[str, str] = { + "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -361,7 +382,11 @@ def restart_code_context( context_id = context.id if isinstance(context, Context) else context try: - headers: Dict[str, str] = {"Content-Type": "application/json"} + headers: Dict[str, str] = { + "Content-Type": "application/json", + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: diff --git a/python/tests/test_sandbox_url.py b/python/tests/test_sandbox_url.py new file mode 100644 index 00000000..422cc502 --- /dev/null +++ b/python/tests/test_sandbox_url.py @@ -0,0 +1,38 @@ +import pytest + +from e2b.connection_config import ConnectionConfig + +from e2b_code_interpreter import AsyncSandbox, Sandbox + + +def make_sandbox(cls, **config_kwargs): + # Constructing a sandbox instance makes no network requests, so URL + # resolution can be tested without a live sandbox. + return cls( + sandbox_id="test-sandbox-id", + sandbox_domain=None, + envd_version="0.2.0", + envd_access_token=None, + connection_config=ConnectionConfig(**config_kwargs), + ) + + +@pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) +async def test_jupyter_url_points_to_sandbox_host_by_default(cls, monkeypatch): + monkeypatch.delenv("E2B_SANDBOX_URL", raising=False) + monkeypatch.delenv("E2B_DEBUG", raising=False) + sandbox = make_sandbox(cls, domain="example.dev") + assert sandbox._jupyter_url == "https://49999-test-sandbox-id.example.dev" + + +@pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) +async def test_jupyter_url_honors_sandbox_url_option(cls): + sandbox = make_sandbox(cls, sandbox_url="https://proxy.example.com") + assert sandbox._jupyter_url == "https://proxy.example.com" + + +@pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) +async def test_jupyter_url_honors_sandbox_url_env_var(cls, monkeypatch): + monkeypatch.setenv("E2B_SANDBOX_URL", "https://env.example.com") + sandbox = make_sandbox(cls) + assert sandbox._jupyter_url == "https://env.example.com" From f1f28003be80ae9557b8f2c908c93dab37660b0c Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:19:15 +0200 Subject: [PATCH 2/3] Address review: strip trailing slash from sandbox URL, send X-Access-Token on context requests Strip trailing slashes when the Jupyter URL comes from the sandbox URL override so path concatenation can't produce double slashes, and send X-Access-Token on the context-CRUD requests in the JS SDK and Python AsyncSandbox, matching run_code and the Python sync Sandbox. Co-Authored-By: Claude Fable 5 --- js/src/sandbox.ts | 22 +++++++++++++++---- js/tests/sandboxUrl.test.ts | 5 +++++ .../code_interpreter_async.py | 10 ++++++++- .../code_interpreter_sync.py | 2 +- python/tests/test_sandbox_url.py | 6 +++++ 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index 00d60c84..bf5ad43c 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -138,10 +138,12 @@ export class Sandbox extends BaseSandbox { 'code-interpreter-v1' protected get jupyterUrl(): string { - return this.connectionConfig.getSandboxDirectUrl(this.sandboxId, { - sandboxDomain: this.sandboxDomain, - envdPort: JUPYTER_PORT, - }) + return this.connectionConfig + .getSandboxDirectUrl(this.sandboxId, { + sandboxDomain: this.sandboxDomain, + envdPort: JUPYTER_PORT, + }) + .replace(/\/+$/, '') } /** @@ -304,6 +306,9 @@ export class Sandbox extends BaseSandbox { if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken } + if (this.envdAccessToken) { + headers['X-Access-Token'] = this.envdAccessToken + } const res = await fetch(`${this.jupyterUrl}/contexts`, { method: 'POST', @@ -346,6 +351,9 @@ export class Sandbox extends BaseSandbox { if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken } + if (this.envdAccessToken) { + headers['X-Access-Token'] = this.envdAccessToken + } const res = await fetch(`${this.jupyterUrl}/contexts/${id}`, { method: 'DELETE', @@ -381,6 +389,9 @@ export class Sandbox extends BaseSandbox { if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken } + if (this.envdAccessToken) { + headers['X-Access-Token'] = this.envdAccessToken + } const res = await fetch(`${this.jupyterUrl}/contexts`, { method: 'GET', @@ -421,6 +432,9 @@ export class Sandbox extends BaseSandbox { if (this.trafficAccessToken) { headers['E2B-Traffic-Access-Token'] = this.trafficAccessToken } + if (this.envdAccessToken) { + headers['X-Access-Token'] = this.envdAccessToken + } const res = await fetch(`${this.jupyterUrl}/contexts/${id}/restart`, { method: 'POST', diff --git a/js/tests/sandboxUrl.test.ts b/js/tests/sandboxUrl.test.ts index 4566f6cd..8e7aa3e7 100644 --- a/js/tests/sandboxUrl.test.ts +++ b/js/tests/sandboxUrl.test.ts @@ -43,6 +43,11 @@ test('jupyterUrl honors the sandboxUrl option', () => { expect(sandbox.jupyterUrl).toBe('https://proxy.example.com') }) +test('jupyterUrl strips trailing slashes from the sandboxUrl option', () => { + const sandbox = createSandbox({ sandboxUrl: 'https://proxy.example.com/' }) + expect(sandbox.jupyterUrl).toBe('https://proxy.example.com') +}) + test('jupyterUrl honors the E2B_SANDBOX_URL environment variable', () => { process.env.E2B_SANDBOX_URL = 'https://env.example.com' const sandbox = createSandbox() diff --git a/python/e2b_code_interpreter/code_interpreter_async.py b/python/e2b_code_interpreter/code_interpreter_async.py index 82a58b51..d4298ce1 100644 --- a/python/e2b_code_interpreter/code_interpreter_async.py +++ b/python/e2b_code_interpreter/code_interpreter_async.py @@ -65,7 +65,7 @@ def _jupyter_url(self) -> str: # variable, same as the base SDK does for envd requests. sandbox_url = self.connection_config._sandbox_url if sandbox_url: - return sandbox_url + return sandbox_url.rstrip("/") return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property @@ -275,6 +275,8 @@ async def create_code_context( "E2b-Sandbox-Id": self.sandbox_id, "E2b-Sandbox-Port": str(JUPYTER_PORT), } + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -316,6 +318,8 @@ async def remove_code_context( "E2b-Sandbox-Id": self.sandbox_id, "E2b-Sandbox-Port": str(JUPYTER_PORT), } + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -346,6 +350,8 @@ async def list_code_contexts(self) -> List[Context]: "E2b-Sandbox-Id": self.sandbox_id, "E2b-Sandbox-Port": str(JUPYTER_PORT), } + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -385,6 +391,8 @@ async def restart_code_context( "E2b-Sandbox-Id": self.sandbox_id, "E2b-Sandbox-Port": str(JUPYTER_PORT), } + if self._envd_access_token: + headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token diff --git a/python/e2b_code_interpreter/code_interpreter_sync.py b/python/e2b_code_interpreter/code_interpreter_sync.py index ddfe4aad..56b4b878 100644 --- a/python/e2b_code_interpreter/code_interpreter_sync.py +++ b/python/e2b_code_interpreter/code_interpreter_sync.py @@ -62,7 +62,7 @@ def _jupyter_url(self) -> str: # variable, same as the base SDK does for envd requests. sandbox_url = self.connection_config._sandbox_url if sandbox_url: - return sandbox_url + return sandbox_url.rstrip("/") return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property diff --git a/python/tests/test_sandbox_url.py b/python/tests/test_sandbox_url.py index 422cc502..25304508 100644 --- a/python/tests/test_sandbox_url.py +++ b/python/tests/test_sandbox_url.py @@ -31,6 +31,12 @@ async def test_jupyter_url_honors_sandbox_url_option(cls): assert sandbox._jupyter_url == "https://proxy.example.com" +@pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) +async def test_jupyter_url_strips_trailing_slashes_from_sandbox_url(cls): + sandbox = make_sandbox(cls, sandbox_url="https://proxy.example.com/") + assert sandbox._jupyter_url == "https://proxy.example.com" + + @pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) async def test_jupyter_url_honors_sandbox_url_env_var(cls, monkeypatch): monkeypatch.setenv("E2B_SANDBOX_URL", "https://env.example.com") From fa2de77539466b41ee83c72ae3dfc7e44f1b966a Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:09:49 +0200 Subject: [PATCH 3/3] Remove trailing-slash stripping from sandbox URL override Callers are expected to pass a well-formed sandbox URL, matching the base SDK, which also returns the override verbatim. Co-Authored-By: Claude Fable 5 --- js/src/sandbox.ts | 10 ++++------ js/tests/sandboxUrl.test.ts | 5 ----- python/e2b_code_interpreter/code_interpreter_async.py | 2 +- python/e2b_code_interpreter/code_interpreter_sync.py | 2 +- python/tests/test_sandbox_url.py | 6 ------ 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/js/src/sandbox.ts b/js/src/sandbox.ts index bf5ad43c..8d3752b9 100644 --- a/js/src/sandbox.ts +++ b/js/src/sandbox.ts @@ -138,12 +138,10 @@ export class Sandbox extends BaseSandbox { 'code-interpreter-v1' protected get jupyterUrl(): string { - return this.connectionConfig - .getSandboxDirectUrl(this.sandboxId, { - sandboxDomain: this.sandboxDomain, - envdPort: JUPYTER_PORT, - }) - .replace(/\/+$/, '') + return this.connectionConfig.getSandboxDirectUrl(this.sandboxId, { + sandboxDomain: this.sandboxDomain, + envdPort: JUPYTER_PORT, + }) } /** diff --git a/js/tests/sandboxUrl.test.ts b/js/tests/sandboxUrl.test.ts index 8e7aa3e7..4566f6cd 100644 --- a/js/tests/sandboxUrl.test.ts +++ b/js/tests/sandboxUrl.test.ts @@ -43,11 +43,6 @@ test('jupyterUrl honors the sandboxUrl option', () => { expect(sandbox.jupyterUrl).toBe('https://proxy.example.com') }) -test('jupyterUrl strips trailing slashes from the sandboxUrl option', () => { - const sandbox = createSandbox({ sandboxUrl: 'https://proxy.example.com/' }) - expect(sandbox.jupyterUrl).toBe('https://proxy.example.com') -}) - test('jupyterUrl honors the E2B_SANDBOX_URL environment variable', () => { process.env.E2B_SANDBOX_URL = 'https://env.example.com' const sandbox = createSandbox() diff --git a/python/e2b_code_interpreter/code_interpreter_async.py b/python/e2b_code_interpreter/code_interpreter_async.py index d4298ce1..db1d2b26 100644 --- a/python/e2b_code_interpreter/code_interpreter_async.py +++ b/python/e2b_code_interpreter/code_interpreter_async.py @@ -65,7 +65,7 @@ def _jupyter_url(self) -> str: # variable, same as the base SDK does for envd requests. sandbox_url = self.connection_config._sandbox_url if sandbox_url: - return sandbox_url.rstrip("/") + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property diff --git a/python/e2b_code_interpreter/code_interpreter_sync.py b/python/e2b_code_interpreter/code_interpreter_sync.py index 56b4b878..ddfe4aad 100644 --- a/python/e2b_code_interpreter/code_interpreter_sync.py +++ b/python/e2b_code_interpreter/code_interpreter_sync.py @@ -62,7 +62,7 @@ def _jupyter_url(self) -> str: # variable, same as the base SDK does for envd requests. sandbox_url = self.connection_config._sandbox_url if sandbox_url: - return sandbox_url.rstrip("/") + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" @property diff --git a/python/tests/test_sandbox_url.py b/python/tests/test_sandbox_url.py index 25304508..422cc502 100644 --- a/python/tests/test_sandbox_url.py +++ b/python/tests/test_sandbox_url.py @@ -31,12 +31,6 @@ async def test_jupyter_url_honors_sandbox_url_option(cls): assert sandbox._jupyter_url == "https://proxy.example.com" -@pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) -async def test_jupyter_url_strips_trailing_slashes_from_sandbox_url(cls): - sandbox = make_sandbox(cls, sandbox_url="https://proxy.example.com/") - assert sandbox._jupyter_url == "https://proxy.example.com" - - @pytest.mark.parametrize("cls", [Sandbox, AsyncSandbox]) async def test_jupyter_url_honors_sandbox_url_env_var(cls, monkeypatch): monkeypatch.setenv("E2B_SANDBOX_URL", "https://env.example.com")