Skip to content

Commit

Permalink
Merge pull request #45 from e2b-dev/change-js-kernel
Browse files Browse the repository at this point in the history
Add Deno kernel
  • Loading branch information
jakubno authored Oct 31, 2024
2 parents e444aa4 + 039f34d commit 5894232
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/lazy-impalas-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@e2b/code-interpreter-template': patch
---

Add [Deno kernel](https://docs.deno.com/runtime/reference/cli/jupyter/)
80 changes: 80 additions & 0 deletions js/tests/languages/deno.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { expect } from 'vitest'

import { sandboxTest } from '../setup'

sandboxTest('js simple', async ({ sandbox }) => {
const result = await sandbox.runCode('console.log("Hello, World!")', {language: "deno"})

expect(result.logs.stdout.join().trim()).toEqual('Hello, World!')
})

sandboxTest('js import', async ({ sandbox }) => {
const result = await sandbox.runCode('import isOdd from "npm:is-odd"\nisOdd(3)', {language: "deno"})

expect(result.results[0].text).toEqual('true')
})

sandboxTest('js top level await', async ({ sandbox }) => {
const result = await sandbox.runCode(`
async function main() {
return 'Hello, World!'
}
await main()
`, {language: "deno"})
expect(result.results[0].text).toEqual('Hello, World!')
})

sandboxTest('js es6', async ({ sandbox }) => {
const result = await sandbox.runCode(`
const add = (x, y) => x + y;
add(1, 2)`, {language: "deno"})
expect(result.results[0].text).toEqual('3')
})


sandboxTest('js context', async ({ sandbox }) => {
await sandbox.runCode('const z = 1', {language: "deno"})
const result = await sandbox.runCode('z', {language: "deno"})
expect(result.results[0].text).toEqual('1')
})

sandboxTest('js cwd', async ({ sandbox }) => {
const result = await sandbox.runCode('process.cwd()', {language: "deno"})
expect(result.results[0].text).toEqual('/home/user')

const ctx = await sandbox.createCodeContext( {cwd: '/home', language: "deno"})
const result2 = await sandbox.runCode('process.cwd()', {context: ctx})
expect(result2.results[0].text).toEqual('/home')
})

sandboxTest('ts simple', async ({ sandbox }) => {
const result = await sandbox.runCode(`
function subtract(x: number, y: number): number {
return x - y;
}
subtract(1, 2)
`, {language: "deno"})

expect(result.results[0].text).toEqual('-1')
})

sandboxTest('test display', async ({ sandbox }) => {
const result = await sandbox.runCode(`
{
[Symbol.for("Jupyter.display")]() {
return {
// Plain text content
"text/plain": "Hello world!",
// HTML output
"text/html": "<h1>Hello world!</h1>",
}
}
}
`, {language: "deno"})

expect(result.results[0].html).toBe('<h1>Hello world!</h1>')
expect(result.results[0].text).toBe('Hello world!')
})
87 changes: 87 additions & 0 deletions python/tests/languages/test_deno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from e2b_code_interpreter import AsyncSandbox


async def test_javascript(async_sandbox: AsyncSandbox):
code = """
console.log('Hello, World!')
"""
execution = await async_sandbox.run_code(code, language="deno")
assert execution.logs.stdout == ["Hello, World!\n"]


async def test_import(async_sandbox: AsyncSandbox):
code = """
import isOdd from 'npm:is-odd'
isOdd(3)
"""
execution = await async_sandbox.run_code(code, language="deno")
assert execution.results[0].text == "true"


async def test_toplevel_await(async_sandbox: AsyncSandbox):
code = """
async function main() {
return 'Hello, World!'
}
await main()
"""
execution = await async_sandbox.run_code(code, language="deno")
assert execution.results[0].text == "Hello, World!"


async def test_es6(async_sandbox: AsyncSandbox):
code = """
const add = (x, y) => x + y;
add(1, 2);
"""
execution = await async_sandbox.run_code(code, language="deno")
assert execution.results[0].text == "3"


async def test_context(async_sandbox: AsyncSandbox):
await async_sandbox.run_code("const x = 1", language="deno")
execution = await async_sandbox.run_code("x", language="deno")
assert execution.results[0].text == "1"


async def test_cwd(async_sandbox: AsyncSandbox):
execution = await async_sandbox.run_code("process.cwd()", language="deno")
assert execution.results[0].text == "/home/user"

ctx = await async_sandbox.create_code_context("/home", language="deno")
execution = await async_sandbox.run_code("process.cwd()", context=ctx)
assert execution.results[0].text == "/home"


async def test_typescript(async_sandbox: AsyncSandbox):
execution = await async_sandbox.run_code(
"""
function subtract(x: number, y: number): number {
return x - y;
}
subtract(1, 2);
""",
language="deno",
)
assert execution.results[0].text == "-1"


async def test_display(async_sandbox: AsyncSandbox):
code = """
{
[Symbol.for("Jupyter.display")]() {
return {
// Plain text content
"text/plain": "Hello world!",
// HTML output
"text/html": "<h1>Hello world!</h1>",
}
}
}
"""
execution = await async_sandbox.run_code(code, language="deno")
assert execution.results[0].text == "Hello world!"
assert execution.results[0].html == "<h1>Hello world!</h1>"
14 changes: 14 additions & 0 deletions python/tests/sync/test_default_kernels.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import pytest

from e2b_code_interpreter.code_interpreter_sync import Sandbox


def test_js_kernel(sandbox: Sandbox):
execution = sandbox.run_code("console.log('Hello, World!')", language="js")
assert execution.logs.stdout == ["Hello, World!\n"]


@pytest.mark.skip_debug()
def test_r_kernel(sandbox: Sandbox):
execution = sandbox.run_code('print("Hello, World!")', language="r")
assert execution.logs.stdout == ['[1] "Hello, World!"\n']


@pytest.mark.skip_debug()
def test_java_kernel(sandbox: Sandbox):
execution = sandbox.run_code('System.out.println("Hello, World!")', language="java")
assert execution.logs.stdout[0] == "Hello, World!"
6 changes: 6 additions & 0 deletions template/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ RUN npm install -g node-gyp
RUN npm install -g --unsafe-perm ijavascript
RUN ijsinstall --install=global

# Deno Kernel
COPY --from=denoland/deno:bin-2.0.4 /deno /usr/bin/deno
RUN chmod +x /usr/bin/deno
RUN deno jupyter --unstable --install
COPY ./deno.json /root/.local/share/jupyter/kernels/deno/kernel.json

# Bash Kernel
RUN pip install bash_kernel
RUN python -m bash_kernel.install
Expand Down
14 changes: 14 additions & 0 deletions template/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"argv": [
"/usr/bin/deno",
"jupyter",
"--kernel",
"--conn",
"{connection_file}"
],
"display_name": "Deno",
"env": {
"NO_COLOR": "1"
},
"language": "typescript"
}
5 changes: 4 additions & 1 deletion template/server/api/models/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ def __init__(self, is_main_result: bool, data: [str, str]):
self.is_main_result = is_main_result

self.text = data.pop("text/plain", None)
if self.text and self.text.startswith("'") and self.text.endswith("'"):
if self.text and (
(self.text.startswith("'") and self.text.endswith("'"))
or (self.text.startswith('"') and self.text.endswith('"'))
):
self.text = self.text[1:-1]

self.html = data.pop("text/html", None)
Expand Down
5 changes: 2 additions & 3 deletions template/server/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ async def create_context(client, websockets: dict, language: str, cwd: str) -> C
response = await client.post(f"{JUPYTER_BASE_URL}/api/sessions", json=data)

if not response.is_success:
return PlainTextResponse(
raise Exception(
f"Failed to create context: {response.text}",
status_code=500,
)

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

logger.info(f"Setting working directory to {cwd}")
try:
await ws.change_current_directory(cwd)
await ws.change_current_directory(cwd, language)
except ExecutionError as e:
return PlainTextResponse(
"Failed to set working directory",
Expand Down
20 changes: 15 additions & 5 deletions template/server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,13 @@ async def post_execute(request: ExecutionRequest):
context_id = default_websockets.get(language)

if not context_id:
context = await create_context(
client, websockets, language, "/home/user"
)
try:
context = await create_context(
client, websockets, language, "/home/user"
)
except Exception as e:
return PlainTextResponse(str(e), status_code=500)

context_id = context.id
default_websockets[language] = context_id

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

return StreamingListJsonResponse(
ws.execute(request.code, env_vars=request.env_vars)
ws.execute(
request.code,
env_vars=request.env_vars,
)
)


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

return await create_context(client, websockets, language, cwd)
try:
return await create_context(client, websockets, language, cwd)
except Exception as e:
return PlainTextResponse(str(e), status_code=500)


@app.get("/contexts")
Expand Down
Loading

0 comments on commit 5894232

Please sign in to comment.