Skip to content

Commit f8f4ada

Browse files
committed
⬇️🔗 url file download helper method
1 parent 26a65ed commit f8f4ada

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

src/codeboxapi/codebox.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def __new__(cls, *args, **kwargs) -> "CodeBox":
5252
"""
5353
Creates a CodeBox session
5454
"""
55-
api_key = kwargs.get("api_key") or os.getenv("CODEBOX_API_KEY", "local")
55+
api_key = kwargs.get("api_key") or os.getenv("CODEBOX_API_KEY")
56+
# todo make sure "local" is not hardcoded default
5657
if api_key == "local":
5758
return import_module("codeboxapi.local").LocalBox(*args, **kwargs)
5859

@@ -175,6 +176,24 @@ async def ainstall(self, *packages: str) -> str:
175176
)
176177
return " ".join(packages) + " installed successfully"
177178

179+
async def afile_from_url(self, url: str, file_path: str) -> "RemoteFile":
180+
"""
181+
Download a file from a URL to the specified destination in the CodeBox.
182+
Example:
183+
>>> codebox.afile_from_url("https://github.com/org/repo/file.txt", "file.txt")
184+
"""
185+
code = (
186+
"import httpx\n"
187+
"async with httpx.AsyncClient() as client:\n"
188+
f" async with client.stream('GET', '{url}') as response:\n"
189+
" response.raise_for_status()\n"
190+
f" with open('{file_path}', 'wb') as f:\n"
191+
" async for chunk in response.aiter_bytes():\n"
192+
" f.write(chunk)\n"
193+
)
194+
await self.aexec(code)
195+
return await self.adownload(file_path)
196+
178197
async def alist_files(self) -> list["RemoteFile"]:
179198
from .types import RemoteFile
180199

@@ -246,6 +265,9 @@ def healthcheck(self) -> str:
246265
def install(self, *packages: str) -> str:
247266
return syncify(self.ainstall)(*packages)
248267

268+
def file_from_url(self, url: str, file_path: str) -> "RemoteFile":
269+
return syncify(self.afile_from_url)(url, file_path)
270+
249271
def list_files(self) -> list["RemoteFile"]:
250272
return syncify(self.alist_files)()
251273

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import os
22

33
import pytest
4+
from dotenv import load_dotenv
45

56
from codeboxapi import CodeBox
67

78
LOCALBOX = CodeBox(api_key="local")
89

10+
load_dotenv()
11+
912

1013
@pytest.fixture(
1114
scope="session",

tests/test_v02.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,27 @@ async def test_async_bash_commands(codebox: CodeBox):
312312
assert result.text.strip() == "Hello!", "Execution result should be 'Hello!'"
313313

314314

315+
def test_file_from_url(codebox: CodeBox):
316+
url = "https://raw.githubusercontent.com/shroominic/codebox-api/main/README.md"
317+
file_path = "README.md"
318+
remote_file = codebox.file_from_url(url, file_path)
319+
assert isinstance(remote_file, RemoteFile), "Should return a RemoteFile"
320+
assert remote_file.path == file_path, "File path should match"
321+
assert len(remote_file.get_content()) > 0, "File should have content"
322+
assert file_path in [file.path for file in codebox.list_files()]
323+
324+
325+
@pytest.mark.asyncio
326+
async def test_file_from_url_async(codebox: CodeBox):
327+
url = "https://raw.githubusercontent.com/shroominic/codebox-api/main/README.md"
328+
file_path = "README.md"
329+
remote_file = await codebox.afile_from_url(url, file_path)
330+
assert isinstance(remote_file, RemoteFile), "Should return a RemoteFile"
331+
assert remote_file.path == file_path, "File path should match"
332+
assert len(remote_file.get_content()) > 0, "File should have content"
333+
assert file_path in [file.path for file in await codebox.alist_files()]
334+
335+
315336
def test_local_box_singleton():
316337
from codeboxapi.local import LocalBox
317338

0 commit comments

Comments
 (0)