Skip to content

Commit 5894232

Browse files
authored
Merge pull request #45 from e2b-dev/change-js-kernel
Add Deno kernel
2 parents e444aa4 + 039f34d commit 5894232

File tree

11 files changed

+283
-21
lines changed

11 files changed

+283
-21
lines changed

.changeset/lazy-impalas-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@e2b/code-interpreter-template': patch
3+
---
4+
5+
Add [Deno kernel](https://docs.deno.com/runtime/reference/cli/jupyter/)

js/tests/languages/deno.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { expect } from 'vitest'
2+
3+
import { sandboxTest } from '../setup'
4+
5+
sandboxTest('js simple', async ({ sandbox }) => {
6+
const result = await sandbox.runCode('console.log("Hello, World!")', {language: "deno"})
7+
8+
expect(result.logs.stdout.join().trim()).toEqual('Hello, World!')
9+
})
10+
11+
sandboxTest('js import', async ({ sandbox }) => {
12+
const result = await sandbox.runCode('import isOdd from "npm:is-odd"\nisOdd(3)', {language: "deno"})
13+
14+
expect(result.results[0].text).toEqual('true')
15+
})
16+
17+
sandboxTest('js top level await', async ({ sandbox }) => {
18+
const result = await sandbox.runCode(`
19+
async function main() {
20+
return 'Hello, World!'
21+
}
22+
23+
await main()
24+
`, {language: "deno"})
25+
expect(result.results[0].text).toEqual('Hello, World!')
26+
})
27+
28+
sandboxTest('js es6', async ({ sandbox }) => {
29+
const result = await sandbox.runCode(`
30+
const add = (x, y) => x + y;
31+
add(1, 2)`, {language: "deno"})
32+
expect(result.results[0].text).toEqual('3')
33+
})
34+
35+
36+
sandboxTest('js context', async ({ sandbox }) => {
37+
await sandbox.runCode('const z = 1', {language: "deno"})
38+
const result = await sandbox.runCode('z', {language: "deno"})
39+
expect(result.results[0].text).toEqual('1')
40+
})
41+
42+
sandboxTest('js cwd', async ({ sandbox }) => {
43+
const result = await sandbox.runCode('process.cwd()', {language: "deno"})
44+
expect(result.results[0].text).toEqual('/home/user')
45+
46+
const ctx = await sandbox.createCodeContext( {cwd: '/home', language: "deno"})
47+
const result2 = await sandbox.runCode('process.cwd()', {context: ctx})
48+
expect(result2.results[0].text).toEqual('/home')
49+
})
50+
51+
sandboxTest('ts simple', async ({ sandbox }) => {
52+
const result = await sandbox.runCode(`
53+
function subtract(x: number, y: number): number {
54+
return x - y;
55+
}
56+
57+
subtract(1, 2)
58+
`, {language: "deno"})
59+
60+
expect(result.results[0].text).toEqual('-1')
61+
})
62+
63+
sandboxTest('test display', async ({ sandbox }) => {
64+
const result = await sandbox.runCode(`
65+
{
66+
[Symbol.for("Jupyter.display")]() {
67+
return {
68+
// Plain text content
69+
"text/plain": "Hello world!",
70+
71+
// HTML output
72+
"text/html": "<h1>Hello world!</h1>",
73+
}
74+
}
75+
}
76+
`, {language: "deno"})
77+
78+
expect(result.results[0].html).toBe('<h1>Hello world!</h1>')
79+
expect(result.results[0].text).toBe('Hello world!')
80+
})

python/tests/languages/test_deno.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from e2b_code_interpreter import AsyncSandbox
2+
3+
4+
async def test_javascript(async_sandbox: AsyncSandbox):
5+
code = """
6+
console.log('Hello, World!')
7+
"""
8+
execution = await async_sandbox.run_code(code, language="deno")
9+
assert execution.logs.stdout == ["Hello, World!\n"]
10+
11+
12+
async def test_import(async_sandbox: AsyncSandbox):
13+
code = """
14+
import isOdd from 'npm:is-odd'
15+
isOdd(3)
16+
"""
17+
execution = await async_sandbox.run_code(code, language="deno")
18+
assert execution.results[0].text == "true"
19+
20+
21+
async def test_toplevel_await(async_sandbox: AsyncSandbox):
22+
code = """
23+
async function main() {
24+
return 'Hello, World!'
25+
}
26+
27+
await main()
28+
"""
29+
execution = await async_sandbox.run_code(code, language="deno")
30+
assert execution.results[0].text == "Hello, World!"
31+
32+
33+
async def test_es6(async_sandbox: AsyncSandbox):
34+
code = """
35+
const add = (x, y) => x + y;
36+
add(1, 2);
37+
"""
38+
execution = await async_sandbox.run_code(code, language="deno")
39+
assert execution.results[0].text == "3"
40+
41+
42+
async def test_context(async_sandbox: AsyncSandbox):
43+
await async_sandbox.run_code("const x = 1", language="deno")
44+
execution = await async_sandbox.run_code("x", language="deno")
45+
assert execution.results[0].text == "1"
46+
47+
48+
async def test_cwd(async_sandbox: AsyncSandbox):
49+
execution = await async_sandbox.run_code("process.cwd()", language="deno")
50+
assert execution.results[0].text == "/home/user"
51+
52+
ctx = await async_sandbox.create_code_context("/home", language="deno")
53+
execution = await async_sandbox.run_code("process.cwd()", context=ctx)
54+
assert execution.results[0].text == "/home"
55+
56+
57+
async def test_typescript(async_sandbox: AsyncSandbox):
58+
execution = await async_sandbox.run_code(
59+
"""
60+
function subtract(x: number, y: number): number {
61+
return x - y;
62+
}
63+
64+
subtract(1, 2);
65+
""",
66+
language="deno",
67+
)
68+
assert execution.results[0].text == "-1"
69+
70+
71+
async def test_display(async_sandbox: AsyncSandbox):
72+
code = """
73+
{
74+
[Symbol.for("Jupyter.display")]() {
75+
return {
76+
// Plain text content
77+
"text/plain": "Hello world!",
78+
79+
// HTML output
80+
"text/html": "<h1>Hello world!</h1>",
81+
}
82+
}
83+
}
84+
"""
85+
execution = await async_sandbox.run_code(code, language="deno")
86+
assert execution.results[0].text == "Hello world!"
87+
assert execution.results[0].html == "<h1>Hello world!</h1>"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
import pytest
2+
13
from e2b_code_interpreter.code_interpreter_sync import Sandbox
24

35

46
def test_js_kernel(sandbox: Sandbox):
57
execution = sandbox.run_code("console.log('Hello, World!')", language="js")
68
assert execution.logs.stdout == ["Hello, World!\n"]
9+
10+
11+
@pytest.mark.skip_debug()
12+
def test_r_kernel(sandbox: Sandbox):
13+
execution = sandbox.run_code('print("Hello, World!")', language="r")
14+
assert execution.logs.stdout == ['[1] "Hello, World!"\n']
15+
16+
17+
@pytest.mark.skip_debug()
18+
def test_java_kernel(sandbox: Sandbox):
19+
execution = sandbox.run_code('System.out.println("Hello, World!")', language="java")
20+
assert execution.logs.stdout[0] == "Hello, World!"

template/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ RUN npm install -g node-gyp
2727
RUN npm install -g --unsafe-perm ijavascript
2828
RUN ijsinstall --install=global
2929

30+
# Deno Kernel
31+
COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno
32+
RUN chmod +x /usr/bin/deno
33+
RUN deno jupyter --unstable --install
34+
COPY ./deno.json /root/.local/share/jupyter/kernels/deno/kernel.json
35+
3036
# Bash Kernel
3137
RUN pip install bash_kernel
3238
RUN python -m bash_kernel.install

template/deno.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"argv": [
3+
"/usr/bin/deno",
4+
"jupyter",
5+
"--kernel",
6+
"--conn",
7+
"{connection_file}"
8+
],
9+
"display_name": "Deno",
10+
"env": {
11+
"NO_COLOR": "1"
12+
},
13+
"language": "typescript"
14+
}

template/server/api/models/result.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ def __init__(self, is_main_result: bool, data: [str, str]):
4545
self.is_main_result = is_main_result
4646

4747
self.text = data.pop("text/plain", None)
48-
if self.text and self.text.startswith("'") and self.text.endswith("'"):
48+
if self.text and (
49+
(self.text.startswith("'") and self.text.endswith("'"))
50+
or (self.text.startswith('"') and self.text.endswith('"'))
51+
):
4952
self.text = self.text[1:-1]
5053

5154
self.html = data.pop("text/html", None)

template/server/contexts.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ async def create_context(client, websockets: dict, language: str, cwd: str) -> C
3636
response = await client.post(f"{JUPYTER_BASE_URL}/api/sessions", json=data)
3737

3838
if not response.is_success:
39-
return PlainTextResponse(
39+
raise Exception(
4040
f"Failed to create context: {response.text}",
41-
status_code=500,
4241
)
4342

4443
session_data = response.json()
@@ -53,7 +52,7 @@ async def create_context(client, websockets: dict, language: str, cwd: str) -> C
5352

5453
logger.info(f"Setting working directory to {cwd}")
5554
try:
56-
await ws.change_current_directory(cwd)
55+
await ws.change_current_directory(cwd, language)
5756
except ExecutionError as e:
5857
return PlainTextResponse(
5958
"Failed to set working directory",

template/server/main.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,13 @@ async def post_execute(request: ExecutionRequest):
8989
context_id = default_websockets.get(language)
9090

9191
if not context_id:
92-
context = await create_context(
93-
client, websockets, language, "/home/user"
94-
)
92+
try:
93+
context = await create_context(
94+
client, websockets, language, "/home/user"
95+
)
96+
except Exception as e:
97+
return PlainTextResponse(str(e), status_code=500)
98+
9599
context_id = context.id
96100
default_websockets[language] = context_id
97101

@@ -110,7 +114,10 @@ async def post_execute(request: ExecutionRequest):
110114
)
111115

112116
return StreamingListJsonResponse(
113-
ws.execute(request.code, env_vars=request.env_vars)
117+
ws.execute(
118+
request.code,
119+
env_vars=request.env_vars,
120+
)
114121
)
115122

116123

@@ -121,7 +128,10 @@ async def post_contexts(request: CreateContext) -> Context:
121128
language = normalize_language(request.language)
122129
cwd = request.cwd or "/home/user"
123130

124-
return await create_context(client, websockets, language, cwd)
131+
try:
132+
return await create_context(client, websockets, language, cwd)
133+
except Exception as e:
134+
return PlainTextResponse(str(e), status_code=500)
125135

126136

127137
@app.get("/contexts")

0 commit comments

Comments
 (0)