Skip to content

Commit bf2cc51

Browse files
committed
one bug further lol
1 parent 718e41f commit bf2cc51

4 files changed

Lines changed: 26 additions & 34 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FROM ghcr.io/astral-sh/uv as uv
22

3-
FROM --platform=arm64 python:3.11 as build
3+
FROM --platform=amd64 python:3.11 as build
44

55
ENV VIRTUAL_ENV=/.venv PATH="/.venv/bin:$PATH"
66

src/codeboxapi/api.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
from contextlib import asynccontextmanager
33
from datetime import datetime, timedelta
44
from os import getenv
5+
from tempfile import SpooledTemporaryFile
56
from typing import AsyncGenerator, Literal
67

7-
from fastapi import Depends, FastAPI, HTTPException, UploadFile
8+
from fastapi import Body, Depends, FastAPI, HTTPException, UploadFile
89
from fastapi.responses import StreamingResponse
910
from pydantic import BaseModel
1011

@@ -18,8 +19,9 @@
1819
@asynccontextmanager
1920
async def lifespan(_: FastAPI) -> AsyncGenerator[None, None]:
2021
async def timeout():
21-
timeout_secs = float(getenv("CODEBOX_TIMEOUT", "900"))
22-
while last_interaction + timedelta(seconds=timeout_secs) > datetime.utcnow():
22+
if (_timeout := getenv("CODEBOX_TIMEOUT", "90")).lower() == "none":
23+
return
24+
while last_interaction + timedelta(seconds=float(_timeout)) > datetime.utcnow():
2325
await asyncio.sleep(1)
2426
exit(0)
2527

@@ -28,18 +30,14 @@ async def timeout():
2830
t.cancel()
2931

3032

31-
app = FastAPI(title="Codebox API", lifespan=lifespan)
32-
33-
3433
async def get_codebox() -> AsyncGenerator[LocalBox, None]:
3534
global codebox, last_interaction
3635
last_interaction = datetime.utcnow()
3736
yield codebox
3837

3938

40-
@app.get("/")
41-
async def healthcheck() -> dict[str, str]:
42-
return {"status": "ok"}
39+
app = FastAPI(title="Codebox API", lifespan=lifespan)
40+
app.get("/")(lambda: {"status": "ok"})
4341

4442

4543
class ExecBody(BaseModel):
@@ -62,7 +60,7 @@ async def event_stream() -> AsyncGenerator[str, None]:
6260
return StreamingResponse(event_stream())
6361

6462

65-
@app.get("/download/{file_name}")
63+
@app.get("/files/download/{file_name}")
6664
async def download(
6765
file_name: str,
6866
timeout: int | None = None,
@@ -71,21 +69,32 @@ async def download(
7169
return StreamingResponse(codebox.astream_download(file_name, timeout))
7270

7371

74-
@app.post("/upload")
72+
@app.post("/files/upload")
7573
async def upload(
7674
file: UploadFile,
7775
timeout: int | None = None,
7876
codebox: LocalBox = Depends(get_codebox),
7977
) -> "CodeBoxFile":
8078
if not file.filename:
8179
raise HTTPException(status_code=400, detail="A file name is required")
80+
if isinstance(file.file, SpooledTemporaryFile):
81+
file.file = file.file
8282
return await codebox.aupload(file.filename, file.file, timeout)
8383

8484

85+
@app.post("/code/execute")
86+
async def deprecated_exec(
87+
body: dict = Body(), codebox: LocalBox = Depends(get_codebox)
88+
) -> dict:
89+
"""deprecated: use /exec instead"""
90+
ex = await codebox.aexec(body["properties"]["code"])
91+
return {"properties": {"stdout": ex.text, "stderr": ex.errors, "result": ex.text}}
92+
93+
8594
def serve():
8695
import uvicorn
8796

88-
uvicorn.run(app, host="0.0.0.0", port=getenv("CODEBOX_PORT", 8069))
97+
uvicorn.run(app, host="0.0.0.0", port=8000)
8998

9099

91100
if __name__ == "__main__":

src/codeboxapi/docker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def __init__(
4343
"-e",
4444
f"CODEBOX_TIMEOUT={timeout}",
4545
"-p",
46-
f"{self.port}:8069",
46+
f"{self.port}:8000",
4747
image,
4848
],
4949
check=True,

src/codeboxapi/remote.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,21 +100,6 @@ async def astream_exec(
100100
async for c in self.astream_exec(code, kernel, timeout, cwd):
101101
yield c
102102

103-
def upload(
104-
self,
105-
remote_file_path: str,
106-
content: BinaryIO | bytes | str,
107-
timeout: float | None = None,
108-
) -> CodeBoxFile:
109-
if isinstance(content, str):
110-
content = content.encode("utf-8")
111-
response = self.client.post(
112-
url="/upload",
113-
files={"file": (remote_file_path, content)},
114-
timeout=timeout,
115-
)
116-
return CodeBoxFile(**response.json())
117-
118103
async def aupload(
119104
self,
120105
file_name: str,
@@ -124,7 +109,7 @@ async def aupload(
124109
if isinstance(content, str):
125110
content = content.encode("utf-8")
126111
response = await self.aclient.post(
127-
url="/upload",
112+
url="/files/upload",
128113
files={"file": (file_name, content)},
129114
timeout=timeout,
130115
)
@@ -137,9 +122,8 @@ def stream_download(
137122
) -> Generator[bytes, None, None]:
138123
with self.client.stream(
139124
method="GET",
140-
url="/download",
125+
url=f"/files/download/{remote_file_path}",
141126
timeout=timeout,
142-
params={"file_name": remote_file_path},
143127
) as response:
144128
for chunk in response.iter_bytes():
145129
yield chunk
@@ -151,9 +135,8 @@ async def astream_download(
151135
) -> AsyncGenerator[bytes, None]:
152136
async with self.aclient.stream(
153137
method="GET",
154-
url="/download",
138+
url=f"/files/download/{remote_file_path}",
155139
timeout=timeout,
156-
params={"file_name": remote_file_path},
157140
) as response:
158141
async for chunk in response.aiter_bytes():
159142
yield chunk

0 commit comments

Comments
 (0)