diff --git a/js/tests/envVars.test.ts b/js/tests/envVars.test.ts deleted file mode 100644 index a4d7f3ca..00000000 --- a/js/tests/envVars.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { expect } from 'vitest' - -import { isDebug, sandboxTest } from './setup' -import { Sandbox } from '../src' - -// Skip this test if we are running in debug mode — the pwd and user in the testing docker container are not the same as in the actual sandbox. -sandboxTest.skipIf(isDebug)('env vars', async () => { - const sandbox = await Sandbox.create({ - envs: { TEST_ENV_VAR: 'supertest' }, - }) - - try { - const result = await sandbox.runCode( - `import os; x = os.getenv('TEST_ENV_VAR'); x` - ) - - expect(result.results[0].text.trim()).toEqual('supertest') - } finally { - await sandbox.kill() - } -}) - -sandboxTest('env vars on sandbox', async ({ sandbox }) => { - const result = await sandbox.runCode( - "import os; os.getenv('FOO')", - { envs: { FOO: 'bar' } } - ) - - expect(result.results[0].text.trim()).toEqual('bar') -}) - -sandboxTest('env vars on sandbox override', async () => { - const sandbox = await Sandbox.create({ - envs: { FOO: 'bar', SBX: 'value' }, - }) - - try { - await sandbox.runCode( - "import os; os.environ['FOO'] = 'bar'; os.environ['RUNTIME_ENV'] = 'js_runtime'" - ) - const result = await sandbox.runCode( - "import os; os.getenv('FOO')", - { envs: { FOO: 'baz' } } - ) - - expect(result.results[0].text.trim()).toEqual('baz') - - const result2 = await sandbox.runCode( - "import os; os.getenv('RUNTIME_ENV')" - ) - expect(result2.results[0].text.trim()).toEqual('js_runtime') - - if (!isDebug) { - const result3 = await sandbox.runCode( - "import os; os.getenv('SBX')" - ) - expect(result3.results[0].text.trim()).toEqual('value') - } - - const result4 = await sandbox.runCode("import os; os.getenv('FOO')") - expect(result4.results[0].text.trim()).toEqual('bar') - } finally { - await sandbox.kill() - } -}) diff --git a/js/tests/env_vars/bash.test.ts b/js/tests/env_vars/bash.test.ts new file mode 100644 index 00000000..59c8aada --- /dev/null +++ b/js/tests/env_vars/bash.test.ts @@ -0,0 +1,69 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// Bash Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (bash)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `echo $TEST_ENV_VAR`, + { + language: 'bash', + } + ) + + expect(result.logs.stdout[0]).toEqual('supertest\n') + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (bash)', async ({ sandbox }) => { + const result = await sandbox.runCode('echo $FOO', { + envs: { FOO: 'bar' }, + language: 'bash', + }) + + const result_empty = await sandbox.runCode( + 'echo ${FOO:-default}', + { + language: 'bash', + } + ) + + expect(result.logs.stdout[0]).toEqual('bar\n') + expect(result_empty.logs.stdout[0]).toEqual('default\n') +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `echo $TEST_ENV_VAR`, + { + language: 'bash', + envs: { TEST_ENV_VAR: 'overwrite' }, + } + ) + + const result_global_default = await sandbox.runCode( + `echo $TEST_ENV_VAR`, + { + language: 'bash', + } + ) + + expect(result.logs.stdout[0]).toEqual('overwrite\n') + expect(result_global_default.logs.stdout[0]).toEqual('supertest\n') + } finally { + await sandbox.kill() + } +}) diff --git a/js/tests/env_vars/deno.test.ts b/js/tests/env_vars/deno.test.ts new file mode 100644 index 00000000..02fa3491 --- /dev/null +++ b/js/tests/env_vars/deno.test.ts @@ -0,0 +1,69 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// Deno Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (deno)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `Deno.env.get('TEST_ENV_VAR')`, + { + language: 'deno', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (deno)', async ({ sandbox }) => { + const result = await sandbox.runCode("Deno.env.get('FOO')", { + envs: { FOO: 'bar' }, + language: 'deno', + }) + + const result_empty = await sandbox.runCode( + "Deno.env.get('FOO') ?? 'default'", + { + language: 'deno', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('bar') + expect(result_empty.results[0]?.text.trim()).toEqual('default') +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `Deno.env.get('TEST_ENV_VAR')`, + { + language: 'deno', + envs: { TEST_ENV_VAR: 'overwrite' }, + } + ) + + const result_global_default = await sandbox.runCode( + `Deno.env.get('TEST_ENV_VAR')`, + { + language: 'deno', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('overwrite') + expect(result_global_default.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) diff --git a/js/tests/env_vars/java.test.ts b/js/tests/env_vars/java.test.ts new file mode 100644 index 00000000..d9294deb --- /dev/null +++ b/js/tests/env_vars/java.test.ts @@ -0,0 +1,72 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// Java Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (java)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `System.getProperty("TEST_ENV_VAR")`, + { + language: 'java', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (java)', async ({ sandbox }) => { + const result = await sandbox.runCode( + `System.getProperty("FOO")`, + { + envs: { FOO: 'bar' }, + language: 'java', + } + ) + + const result_empty = await sandbox.runCode( + `System.getProperty("FOO", "default")`, + { + language: 'java', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('bar') + expect(result_empty.results[0]?.text.trim()).toEqual('default') +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `System.getProperty("TEST_ENV_VAR")`, + { + language: 'java', + envs: { TEST_ENV_VAR: 'overwrite' }, + } + ) + + const result_global_default = await sandbox.runCode( + `System.getProperty("TEST_ENV_VAR")`, + { + language: 'java', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('overwrite') + expect(result_global_default.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) diff --git a/js/tests/env_vars/js.test.ts b/js/tests/env_vars/js.test.ts new file mode 100644 index 00000000..74c63a72 --- /dev/null +++ b/js/tests/env_vars/js.test.ts @@ -0,0 +1,69 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// JavaScript Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (javascript)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `process.env.TEST_ENV_VAR`, + { + language: 'javascript', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (javascript)', async ({ sandbox }) => { + const result = await sandbox.runCode("process.env.FOO", { + envs: { FOO: 'bar' }, + language: 'javascript', + }) + + const result_empty = await sandbox.runCode( + "process.env.FOO || 'default'", + { + language: 'javascript', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('bar') + expect(result_empty.results[0]?.text.trim()).toEqual('default') +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `process.env.TEST_ENV_VAR`, + { + language: 'javascript', + envs: { TEST_ENV_VAR: 'overwrite' }, + } + ) + + const result_global_default = await sandbox.runCode( + `process.env.TEST_ENV_VAR`, + { + language: 'javascript', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('overwrite') + expect(result_global_default.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) diff --git a/js/tests/env_vars/python.test.ts b/js/tests/env_vars/python.test.ts new file mode 100644 index 00000000..b935bcb3 --- /dev/null +++ b/js/tests/env_vars/python.test.ts @@ -0,0 +1,69 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// Python Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (python)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `import os; os.getenv('TEST_ENV_VAR')`, + { + language: 'python', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (python)', async ({ sandbox }) => { + const result = await sandbox.runCode("import os; os.getenv('FOO')", { + envs: { FOO: 'bar' }, + language: 'python', + }) + + const result_empty = await sandbox.runCode( + "import os; os.getenv('FOO', 'default')", + { + language: 'python', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('bar') + expect(result_empty.results[0]?.text.trim()).toEqual('default') +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode( + `import os; os.getenv('TEST_ENV_VAR')`, + { + language: 'python', + envs: { TEST_ENV_VAR: 'overwrite' }, + } + ) + + const result_global_default = await sandbox.runCode( + `import os; os.getenv('TEST_ENV_VAR')`, + { + language: 'python', + } + ) + + expect(result.results[0]?.text.trim()).toEqual('overwrite') + expect(result_global_default.results[0]?.text.trim()).toEqual('supertest') + } finally { + await sandbox.kill() + } +}) diff --git a/js/tests/env_vars/r.test.ts b/js/tests/env_vars/r.test.ts new file mode 100644 index 00000000..88856fb2 --- /dev/null +++ b/js/tests/env_vars/r.test.ts @@ -0,0 +1,60 @@ +import { expect } from 'vitest' + +import { isDebug, sandboxTest } from '../setup' +import { Sandbox } from '../../src' + +// R Env Vars +sandboxTest.skipIf(isDebug)('env vars on sandbox (R)', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode(`Sys.getenv("TEST_ENV_VAR")`, { + language: 'r', + }) + + expect(result.results[0]?.text.trim()).toEqual(`[1] "supertest"`) + } finally { + await sandbox.kill() + } +}) + +sandboxTest('env vars per execution (R)', async ({ sandbox }) => { + const result = await sandbox.runCode('Sys.getenv("FOO")', { + envs: { FOO: 'bar' }, + language: 'r', + }) + + const result_empty = await sandbox.runCode( + 'Sys.getenv("FOO", unset = "default")', + { + language: 'r', + } + ) + + expect(result.results[0]?.text.trim()).toEqual(`[1] "bar"`) + expect(result_empty.results[0]?.text.trim()).toEqual(`[1] "default"`) +}) + +sandboxTest.skipIf(isDebug)('env vars overwrite', async () => { + const sandbox = await Sandbox.create({ + envs: { TEST_ENV_VAR: 'supertest' }, + }) + + try { + const result = await sandbox.runCode(`Sys.getenv("TEST_ENV_VAR")`, { + language: 'r', + envs: { TEST_ENV_VAR: 'overwrite' }, + }) + + const result_global_default = await sandbox.runCode(`Sys.getenv("TEST_ENV_VAR")`, { + language: 'r', + }) + + expect(result.results[0]?.text.trim()).toEqual(`[1] "overwrite"`) + expect(result_global_default.results[0]?.text.trim()).toEqual(`[1] "supertest"`) + } finally { + await sandbox.kill() + } +}) diff --git a/python/tests/async/env_vars/test_bash.py b/python/tests/async/env_vars/test_bash.py new file mode 100644 index 00000000..78b303b8 --- /dev/null +++ b/python/tests/async/env_vars/test_bash.py @@ -0,0 +1,48 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox +from typing import AsyncGenerator + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash" + ) + assert result.logs.stdout[0] == "supertest\n" + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + "echo $FOO", + envs={"FOO": "bar"}, + language="bash" + ) + + result_empty = await sandbox.run_code( + "echo ${FOO:-default}", + language="bash" + ) + + assert result.logs.stdout[0] == "bar\n" + assert result_empty.logs.stdout[0] == "default\n" + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash" + ) + assert result.logs.stdout[0] == "overwrite\n" + assert result_global_default.logs.stdout[0] == "supertest\n" + finally: + await sandbox.kill() diff --git a/python/tests/async/env_vars/test_deno.py b/python/tests/async/env_vars/test_deno.py new file mode 100644 index 00000000..7df82d21 --- /dev/null +++ b/python/tests/async/env_vars/test_deno.py @@ -0,0 +1,52 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno" + ) + assert result.text is not None + assert result.text.strip() == "supertest" + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + "Deno.env.get('FOO')", + envs={"FOO": "bar"}, + language="deno" + ) + + result_empty = await sandbox.run_code( + "Deno.env.get('FOO') || 'default'", + language="deno" + ) + + assert result.text is not None + assert result.text.strip() == "bar" + assert result_empty.text is not None + assert result_empty.text.strip() == "default" + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno" + ) + assert result.text is not None + assert result.text.strip() == "overwrite" + assert result_global_default.text is not None + assert result_global_default.text.strip() == "supertest" + finally: + await sandbox.kill() diff --git a/python/tests/async/env_vars/test_java.py b/python/tests/async/env_vars/test_java.py new file mode 100644 index 00000000..d48c95e9 --- /dev/null +++ b/python/tests/async/env_vars/test_java.py @@ -0,0 +1,52 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java" + ) + assert result.text is not None + assert result.text.strip() == "supertest" + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + 'System.getProperty("FOO")', + envs={"FOO": "bar"}, + language="java" + ) + + result_empty = await sandbox.run_code( + 'System.getProperty("FOO", "default")', + language="java" + ) + + assert result.text is not None + assert result.text.strip() == "bar" + assert result_empty.text is not None + assert result_empty.text.strip() == "default" + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java" + ) + assert result.text is not None + assert result.text.strip() == "overwrite" + assert result_global_default.text is not None + assert result_global_default.text.strip() == "supertest" + finally: + await sandbox.kill() diff --git a/python/tests/async/env_vars/test_js.py b/python/tests/async/env_vars/test_js.py new file mode 100644 index 00000000..884f9934 --- /dev/null +++ b/python/tests/async/env_vars/test_js.py @@ -0,0 +1,52 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript" + ) + assert result.text is not None + assert result.text.strip() == "supertest" + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + "process.env.FOO", + envs={"FOO": "bar"}, + language="javascript" + ) + + result_empty = await sandbox.run_code( + "process.env.FOO || 'default'", + language="javascript" + ) + + assert result.text is not None + assert result.text.strip() == "bar" + assert result_empty.text is not None + assert result_empty.text.strip() == "default" + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript" + ) + assert result.text is not None + assert result.text.strip() == "overwrite" + assert result_global_default.text is not None + assert result_global_default.text.strip() == "supertest" + finally: + await sandbox.kill() diff --git a/python/tests/async/env_vars/test_python.py b/python/tests/async/env_vars/test_python.py new file mode 100644 index 00000000..0302e197 --- /dev/null +++ b/python/tests/async/env_vars/test_python.py @@ -0,0 +1,52 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python" + ) + assert result.text is not None + assert result.text.strip() == "supertest" + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + "import os; os.getenv('FOO')", + envs={"FOO": "bar"}, + language="python" + ) + + result_empty = await sandbox.run_code( + "import os; os.getenv('FOO', 'default')", + language="python" + ) + + assert result.text is not None + assert result.text.strip() == "bar" + assert result_empty.text is not None + assert result_empty.text.strip() == "default" + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python" + ) + assert result.text is not None + assert result.text.strip() == "overwrite" + assert result_global_default.text is not None + assert result_global_default.text.strip() == "supertest" + finally: + await sandbox.kill() diff --git a/python/tests/async/env_vars/test_r.py b/python/tests/async/env_vars/test_r.py new file mode 100644 index 00000000..ee551d85 --- /dev/null +++ b/python/tests/async/env_vars/test_r.py @@ -0,0 +1,52 @@ +import pytest +from e2b_code_interpreter.code_interpreter_async import AsyncSandbox + +@pytest.mark.skip_debug() +async def test_env_vars_on_sandbox(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + 'Sys.getenv("TEST_ENV_VAR")', + language="r" + ) + assert result.text is not None + assert result.text.strip() == '[1] "supertest"' + finally: + await sandbox.kill() + +async def test_env_vars_per_execution(sandbox: AsyncSandbox): + result = await sandbox.run_code( + 'Sys.getenv("FOO")', + envs={"FOO": "bar"}, + language="r" + ) + + result_empty = await sandbox.run_code( + 'Sys.getenv("FOO", unset = "default")', + language="r" + ) + + assert result.text is not None + assert result.text.strip() == '[1] "bar"' + assert result_empty.text is not None + assert result_empty.text.strip() == '[1] "default"' + +@pytest.mark.skip_debug() +async def test_env_vars_overwrite(): + sandbox = await AsyncSandbox.create(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = await sandbox.run_code( + 'Sys.getenv("TEST_ENV_VAR")', + language="r", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = await sandbox.run_code( + 'Sys.getenv("TEST_ENV_VAR")', + language="r" + ) + assert result.text is not None + assert result.text.strip() == '[1] "overwrite"' + assert result_global_default.text is not None + assert result_global_default.text.strip() == "supertest" + finally: + await sandbox.kill() diff --git a/python/tests/async/test_async_env_vars.py b/python/tests/async/test_async_env_vars.py deleted file mode 100644 index e9ed59c8..00000000 --- a/python/tests/async/test_async_env_vars.py +++ /dev/null @@ -1,46 +0,0 @@ -import pytest - -from e2b_code_interpreter.code_interpreter_async import AsyncSandbox - - -# @pytest.mark.skip_debug() -# async def test_env_vars_sandbox(): -# sbx = await AsyncSandbox.create(envs={"FOO": "bar"}) -# try: -# result = await sbx.run_code("import os; os.getenv('FOO')") -# assert result.text == "bar" -# finally: -# await sbx.kill() - - -async def test_env_vars_in_run_code(async_sandbox: AsyncSandbox): - result = await async_sandbox.run_code( - "import os; os.getenv('FOO')", envs={"FOO": "bar"} - ) - assert result.text == "bar" - - -# -# async def test_env_vars_override(debug: bool): -# sbx = await AsyncSandbox.create(envs={"FOO": "bar", "SBX": "value"}) -# -# try: -# await sbx.run_code( -# "import os; os.environ['FOO'] = 'bar'; os.environ['RUNTIME_ENV'] = 'async_python_runtime'" -# ) -# result = await sbx.run_code("import os; os.getenv('FOO')", envs={"FOO": "baz"}) -# assert result.text == "baz" -# -# # This can fail if running in debug mode (there's a race condition with the restart kernel test) -# result = await sbx.run_code("import os; os.getenv('RUNTIME_ENV')") -# assert result.text == "async_python_runtime" -# -# if not debug: -# result = await sbx.run_code("import os; os.getenv('SBX')") -# assert result.text == "value" -# -# # This can fail if running in debug mode (there's a race condition with the restart kernel test) -# result = await sbx.run_code("import os; os.getenv('FOO')") -# assert result.text == "bar" -# finally: -# await sbx.kill() diff --git a/python/tests/sync/env_vars/test_bash.py b/python/tests/sync/env_vars/test_bash.py new file mode 100644 index 00000000..1fec84c1 --- /dev/null +++ b/python/tests/sync/env_vars/test_bash.py @@ -0,0 +1,47 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash" + ) + assert result.logs.stdout[0] == "supertest\n" + finally: + sandbox.kill() + +def test_env_vars_per_execution(sandbox: Sandbox): + result = sandbox.run_code( + "echo $FOO", + envs={"FOO": "bar"}, + language="bash" + ) + + result_empty = sandbox.run_code( + "echo ${FOO:-default}", + language="bash" + ) + + assert result.logs.stdout[0] == "bar\n" + assert result_empty.logs.stdout[0] == "default\n" + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + "echo $TEST_ENV_VAR", + language="bash" + ) + assert result.logs.stdout[0] == "overwrite\n" + assert result_global_default.logs.stdout[0] == "supertest\n" + finally: + sandbox.kill() diff --git a/python/tests/sync/env_vars/test_deno.py b/python/tests/sync/env_vars/test_deno.py new file mode 100644 index 00000000..03ba4d0c --- /dev/null +++ b/python/tests/sync/env_vars/test_deno.py @@ -0,0 +1,51 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno" + ) + assert result.results[0].text.strip() == "supertest" + finally: + sandbox.kill() + +def test_env_vars_per_execution(): + sandbox = Sandbox() + try: + result = sandbox.run_code( + "Deno.env.get('FOO')", + envs={"FOO": "bar"}, + language="deno" + ) + + result_empty = sandbox.run_code( + "Deno.env.get('FOO') || 'default'", + language="deno" + ) + + assert result.results[0].text.strip() == "bar" + assert result_empty.results[0].text.strip() == "default" + finally: + sandbox.kill() + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + "const x = Deno.env.get('TEST_ENV_VAR'); x", + language="deno" + ) + assert result.results[0].text.strip() == "overwrite" + assert result_global_default.results[0].text.strip() == "supertest" + finally: + sandbox.kill() diff --git a/python/tests/sync/env_vars/test_java.py b/python/tests/sync/env_vars/test_java.py new file mode 100644 index 00000000..8e802283 --- /dev/null +++ b/python/tests/sync/env_vars/test_java.py @@ -0,0 +1,51 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java" + ) + assert result.results[0].text.strip() == "supertest" + finally: + sandbox.kill() + +def test_env_vars_per_execution(): + sandbox = Sandbox() + try: + result = sandbox.run_code( + 'System.getProperty("FOO")', + envs={"FOO": "bar"}, + language="java" + ) + + result_empty = sandbox.run_code( + 'System.getProperty("FOO", "default")', + language="java" + ) + + assert result.results[0].text.strip() == "bar" + assert result_empty.results[0].text.strip() == "default" + finally: + sandbox.kill() + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + 'System.getProperty("TEST_ENV_VAR")', + language="java" + ) + assert result.results[0].text.strip() == "overwrite" + assert result_global_default.results[0].text.strip() == "supertest" + finally: + sandbox.kill() diff --git a/python/tests/sync/env_vars/test_js.py b/python/tests/sync/env_vars/test_js.py new file mode 100644 index 00000000..3c019e7e --- /dev/null +++ b/python/tests/sync/env_vars/test_js.py @@ -0,0 +1,51 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript" + ) + assert result.results[0].text.strip() == "supertest" + finally: + sandbox.kill() + +def test_env_vars_per_execution(): + sandbox = Sandbox() + try: + result = sandbox.run_code( + "process.env.FOO", + envs={"FOO": "bar"}, + language="javascript" + ) + + result_empty = sandbox.run_code( + "process.env.FOO || 'default'", + language="javascript" + ) + + assert result.results[0].text.strip() == "bar" + assert result_empty.results[0].text.strip() == "default" + finally: + sandbox.kill() + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + "process.env.TEST_ENV_VAR", + language="javascript" + ) + assert result.results[0].text.strip() == "overwrite" + assert result_global_default.results[0].text.strip() == "supertest" + finally: + sandbox.kill() diff --git a/python/tests/sync/env_vars/test_python.py b/python/tests/sync/env_vars/test_python.py new file mode 100644 index 00000000..548dc075 --- /dev/null +++ b/python/tests/sync/env_vars/test_python.py @@ -0,0 +1,51 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python" + ) + assert result.results[0].text.strip() == "supertest" + finally: + sandbox.kill() + +def test_env_vars_per_execution(): + sandbox = Sandbox() + try: + result = sandbox.run_code( + "import os; os.getenv('FOO')", + envs={"FOO": "bar"}, + language="python" + ) + + result_empty = sandbox.run_code( + "import os; os.getenv('FOO', 'default')", + language="python" + ) + + assert result.results[0].text.strip() == "bar" + assert result_empty.results[0].text.strip() == "default" + finally: + sandbox.kill() + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + "import os; os.getenv('TEST_ENV_VAR')", + language="python" + ) + assert result.results[0].text.strip() == "overwrite" + assert result_global_default.results[0].text.strip() == "supertest" + finally: + sandbox.kill() diff --git a/python/tests/sync/env_vars/test_r.py b/python/tests/sync/env_vars/test_r.py new file mode 100644 index 00000000..a8ad98b0 --- /dev/null +++ b/python/tests/sync/env_vars/test_r.py @@ -0,0 +1,51 @@ +import pytest +from e2b_code_interpreter import Sandbox + +@pytest.mark.skip_debug() +def test_env_vars_on_sandbox(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "Sys.getenv('TEST_ENV_VAR')", + language="r" + ) + assert result.results[0].text.strip() == '[1] "supertest"' + finally: + sandbox.kill() + +def test_env_vars_per_execution(): + sandbox = Sandbox() + try: + result = sandbox.run_code( + "Sys.getenv('FOO')", + envs={"FOO": "bar"}, + language="r" + ) + + result_empty = sandbox.run_code( + "Sys.getenv('FOO', unset = 'default')", + language="r" + ) + + assert result.results[0].text.strip() == '[1] "bar"' + assert result_empty.results[0].text.strip() == '[1] "default"' + finally: + sandbox.kill() + +@pytest.mark.skip_debug() +def test_env_vars_overwrite(): + sandbox = Sandbox(envs={"TEST_ENV_VAR": "supertest"}) + try: + result = sandbox.run_code( + "Sys.getenv('TEST_ENV_VAR')", + language="r", + envs={"TEST_ENV_VAR": "overwrite"} + ) + result_global_default = sandbox.run_code( + "Sys.getenv('TEST_ENV_VAR')", + language="r" + ) + assert result.results[0].text.strip() == '[1] "overwrite"' + assert result_global_default.results[0].text.strip() == '[1] "supertest"' + finally: + sandbox.kill() diff --git a/python/tests/sync/test_env_vars.py b/python/tests/sync/test_env_vars.py deleted file mode 100644 index 13f87a7b..00000000 --- a/python/tests/sync/test_env_vars.py +++ /dev/null @@ -1,40 +0,0 @@ -import pytest - -from e2b_code_interpreter.code_interpreter_sync import Sandbox - - -# @pytest.mark.skip_debug() -# async def test_env_vars_sandbox(): -# sbx = Sandbox(envs={"FOO": "bar"}) -# result = sbx.run_code("import os; os.getenv('FOO')") -# assert result.text == "bar" -# sbx.kill() - - -async def test_env_vars_in_run_code(sandbox: Sandbox): - result = sandbox.run_code("import os; os.getenv('FOO')", envs={"FOO": "bar"}) - assert result.text == "bar" - - -# -# async def test_env_vars_override(debug: bool): -# sbx = Sandbox(envs={"FOO": "bar", "SBX": "value"}) -# sbx.run_code( -# "import os; os.environ['FOO'] = 'bar'; os.environ['RUNTIME_ENV'] = 'python_runtime'" -# ) -# result = sbx.run_code("import os; os.getenv('FOO')", envs={"FOO": "baz"}) -# assert result.text == "baz" -# -# # This can fail if running in debug mode (there's a race condition with the restart kernel test) -# result = sbx.run_code("import os; os.getenv('RUNTIME_ENV')") -# assert result.text == "python_runtime" -# -# if not debug: -# result = sbx.run_code("import os; os.getenv('SBX')") -# assert result.text == "value" -# -# # This can fail if running in debug mode (there's a race condition with the restart kernel test) -# result = sbx.run_code("import os; os.getenv('FOO')") -# assert result.text == "bar" -# -# sbx.kill() diff --git a/template/server/contexts.py b/template/server/contexts.py index d078dc6e..e6f129e0 100644 --- a/template/server/contexts.py +++ b/template/server/contexts.py @@ -8,6 +8,7 @@ from consts import JUPYTER_BASE_URL from errors import ExecutionError from messaging import ContextWebSocket +from envs import get_envs logger = logging.Logger(__name__) @@ -51,6 +52,7 @@ async def create_context(client, websockets: dict, language: str, cwd: str) -> C session_data = response.json() session_id = session_data["id"] context_id = session_data["kernel"]["id"] + global_env_vars = await get_envs() logger.debug(f"Created context {context_id}") @@ -67,4 +69,12 @@ async def create_context(client, websockets: dict, language: str, cwd: str) -> C status_code=500, ) + try: + await ws.set_env_vars(global_env_vars) + except ExecutionError as e: + return PlainTextResponse( + "Failed to set environment variables", + status_code=500, + ) + return Context(language=language, id=context_id, cwd=cwd) diff --git a/template/server/envs.py b/template/server/envs.py index a8f882af..2456f74d 100644 --- a/template/server/envs.py +++ b/template/server/envs.py @@ -1,12 +1,16 @@ import os -import requests +import httpx LOCAL = os.getenv("E2B_LOCAL", False) ENVD_PORT = 49983 -def get_envs() -> dict: +async def get_envs() -> dict: if LOCAL: - return {} - return requests.get(f"http://localhost:{ENVD_PORT}/envs").json() + return { + "E2B_TEST_VARIABLE": "true" + } + async with httpx.AsyncClient() as client: + response = await client.get(f"http://localhost:{ENVD_PORT}/envs") + return response.json() diff --git a/template/server/main.py b/template/server/main.py index fa7760a9..d28a51fd 100644 --- a/template/server/main.py +++ b/template/server/main.py @@ -34,31 +34,22 @@ async def lifespan(app: FastAPI): global client client = httpx.AsyncClient() - with open("/root/.jupyter/kernel_id") as file: - default_context_id = file.read().strip() - - default_ws = ContextWebSocket( - default_context_id, - str(uuid.uuid4()), - "python", - "/home/user", - ) - default_websockets["python"] = default_context_id - websockets["default"] = default_ws - websockets[default_context_id] = default_ws - - logger.info("Connecting to default runtime") - await default_ws.connect() - - websockets["default"] = default_ws + try: + default_context = await create_context(client, websockets, "python", "/home/user") + default_websockets["python"] = default_context.id + websockets["default"] = websockets[default_context.id] - logger.info("Connected to default runtime") - yield + logger.info("Connected to default runtime") + yield - for ws in websockets.values(): - await ws.close() + # Will cleanup after application shuts down + for ws in websockets.values(): + await ws.close() - await client.aclose() + await client.aclose() + except Exception as e: + logger.error(f"Failed to initialize default context: {e}") + raise app = FastAPI(lifespan=lifespan) diff --git a/template/server/messaging.py b/template/server/messaging.py index 9fe4e081..78882c51 100644 --- a/template/server/messaging.py +++ b/template/server/messaging.py @@ -135,6 +135,86 @@ async def _wait_for_result(self, message_id: str): yield output.model_dump(exclude_none=True) + async def set_env_vars(self, env_vars: Dict[StrictStr, str]): + message_id = str(uuid.uuid4()) + self._executions[message_id] = Execution(in_background=True) + + env_commands = [] + for k, v in env_vars.items(): + if self.language == "python": + env_commands.append(f"import os; os.environ['{k}'] = '{v}'") + elif self.language in ["javascript", "typescript"]: + env_commands.append(f"process.env['{k}'] = '{v}'") + elif self.language == "deno": + env_commands.append(f"Deno.env.set('{k}', '{v}')") + elif self.language == "r": + env_commands.append(f'Sys.setenv({k} = "{v}")') + elif self.language == "java": + env_commands.append(f'System.setProperty("{k}", "{v}")') + elif self.language == "bash": + env_commands.append(f"export {k}='{v}'") + else: + return + + if env_commands: + env_vars_snippet = "\n".join(env_commands) + logger.info(f"Setting env vars: {env_vars_snippet} for {self.language}") + request = self._get_execute_request(message_id, env_vars_snippet, True) + await self._ws.send(request) + + async for item in self._wait_for_result(message_id): + if item["type"] == "error": + raise ExecutionError(f"Error during execution: {item}") + + async def reset_env_vars(self, env_vars: Dict[StrictStr, str]): + global_env_vars = await get_envs() + + # Create a dict of vars to reset and a list of vars to remove + vars_to_reset = {} + vars_to_remove = [] + + for key in env_vars: + if key in global_env_vars: + vars_to_reset[key] = global_env_vars[key] + else: + vars_to_remove.append(key) + + # Reset vars that exist in global env vars + if vars_to_reset: + await self.set_env_vars(vars_to_reset) + + # Remove vars that don't exist in global env vars + if vars_to_remove: + message_id = str(uuid.uuid4()) + self._executions[message_id] = Execution(in_background=True) + + remove_commands = [] + for key in vars_to_remove: + if self.language == "python": + remove_commands.append(f"import os; del os.environ['{key}']") + elif self.language in ["javascript", "typescript"]: + remove_commands.append(f"delete process.env['{key}']") + elif self.language == "deno": + remove_commands.append(f"Deno.env.delete('{key}')") + elif self.language == "r": + remove_commands.append(f"Sys.unsetenv('{key}')") + elif self.language == "java": + remove_commands.append(f'System.clearProperty("{key}")') + elif self.language == "bash": + remove_commands.append(f"unset {key}") + else: + return + + if remove_commands: + remove_snippet = "\n".join(remove_commands) + logger.info(f"Removing env vars: {remove_snippet} for {self.language}") + request = self._get_execute_request(message_id, remove_snippet, True) + await self._ws.send(request) + + async for item in self._wait_for_result(message_id): + if item["type"] == "error": + raise ExecutionError(f"Error during execution: {item}") + async def change_current_directory( self, path: Union[str, StrictStr], language: str ): @@ -178,26 +258,10 @@ async def execute( if self._ws is None: raise Exception("WebSocket not connected") - global_env_vars = get_envs() - env_vars = {**global_env_vars, **env_vars} if env_vars else global_env_vars async with self._lock: + # set env vars (will override global env vars) if env_vars: - vars_to_set = {**global_env_vars, **env_vars} - - # if there is an indent in the code, we need to add the env vars at the beginning of the code - lines = code.split("\n") - indent = 0 - for i, line in enumerate(lines): - if line.strip() != "": - indent = len(line) - len(line.lstrip()) - break - - if self.language == "python": - code = ( - indent * " " - + f"os.environ.set_envs_for_execution({vars_to_set})\n" - + code - ) + await self.set_env_vars(env_vars) logger.info(code) request = self._get_execute_request(message_id, code, False) @@ -211,6 +275,10 @@ async def execute( del self._executions[message_id] + # reset env vars to their previous values, if they were set globally or remove them + if env_vars: + await self.reset_env_vars(env_vars) + async def _receive_message(self): if not self._ws: logger.error("No WebSocket connection") @@ -276,7 +344,7 @@ async def _process_message(self, data: dict): elif data["msg_type"] == "stream": if data["content"]["name"] == "stdout": - logger.debug(f"Execution {parent_msg_ig} received stdout") + logger.debug(f"Execution {parent_msg_ig} received stdout: {data['content']['text']}") await queue.put( Stdout( text=data["content"]["text"], timestamp=data["header"]["date"] @@ -284,7 +352,7 @@ async def _process_message(self, data: dict): ) elif data["content"]["name"] == "stderr": - logger.debug(f"Execution {parent_msg_ig} received stderr") + logger.debug(f"Execution {parent_msg_ig} received stderr: {data['content']['text']}") await queue.put( Stderr( text=data["content"]["text"], timestamp=data["header"]["date"] diff --git a/template/start-up.sh b/template/start-up.sh index 735e5811..5d6223ff 100644 --- a/template/start-up.sh +++ b/template/start-up.sh @@ -13,18 +13,6 @@ function start_jupyter_server() { response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/api/status") done - response=$(curl -s -X POST "localhost:8888/api/sessions" -H "Content-Type: application/json" -d '{"path": "/home/user", "kernel": {"name": "python3"}, "type": "notebook", "name": "default"}') - status=$(echo "${response}" | jq -r '.kernel.execution_state') - if [[ ${status} != "starting" ]]; then - echo "Error creating kernel: ${response} ${status}" - exit 1 - fi - - sudo mkdir -p /root/.jupyter - kernel_id=$(echo "${response}" | jq -r '.kernel.id') - sudo echo "${kernel_id}" | sudo tee /root/.jupyter/kernel_id >/dev/null - sudo echo "${response}" | sudo tee /root/.jupyter/.session_info >/dev/null - cd /root/.server/ /root/.server/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 49999 --workers 1 --no-access-log --no-use-colors --timeout-keep-alive 640 } diff --git a/template/startup_scripts/0001_envs.py b/template/startup_scripts/0001_envs.py deleted file mode 100644 index b58a7bba..00000000 --- a/template/startup_scripts/0001_envs.py +++ /dev/null @@ -1,65 +0,0 @@ -import copy -import os -import IPython - - -class E2BEnviron(os._Environ): - return_values = {} - keys_to_remove = set() - - def __setitem__(self, key, value): - super().__setitem__(key, value) - self.return_values.pop(key, None) - self.keys_to_remove.discard(key) - - def __delitem__(self, key): - super().__delitem__(key) - self.return_values.pop(key, None) - self.keys_to_remove.discard(key) - - def set_envs_for_execution(self, update=None): - update = update or {} - - return_values = { - key: self[key] for key in set(self.keys()).intersection(update.keys()) - } - keys_to_remove = set(update.keys()).difference(self.keys()) - - self.update(update) - - self.return_values = return_values - self.keys_to_remove = keys_to_remove - - def reset_envs_for_execution(self): - keys_to_remove = copy.copy(self.keys_to_remove) - for key in keys_to_remove: - self.pop(key) - - return_values = copy.copy(self.return_values) - self.update(return_values) - - self.keys_to_remove.clear() - self.return_values.clear() - - -e2b_environ = E2BEnviron( - {}, - os.environ.encodekey, - os.environ.decodekey, - os.environ.encodekey, - os.environ.decodekey, -) -e2b_environ.update(os.environ) -os.environ = e2b_environ - - -def reset_envs(*args, **kwargs): - try: - if isinstance(os.environ, E2BEnviron): - os.environ.reset_envs_for_execution() - except Exception as e: - print(f"Failed to reset envs: {e}") - - -ip = IPython.get_ipython() -ip.events.register("post_run_cell", reset_envs)