Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 0 additions & 45 deletions src/deno_sandbox/api_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
ExpandGlobOptions,
MakeTempDirOptions,
MakeTempFileOptions,
FsOpenOptions,
SymlinkOptions,
)

Expand All @@ -41,10 +40,6 @@
AsyncPaginatedList,
PaginatedList,
)
from .wrappers import (
FsFile,
AsyncFsFile,
)


class Apps:
Expand Down Expand Up @@ -471,14 +466,6 @@ def expand_glob(

return result

def create(self, path: str) -> FsFile:
"""Create a new empty file at the specified path."""

params = {"path": path}
result = self._rpc.call("create", params)

return result

def link(self, target: str, path: str) -> None:
"""Create a hard link pointing to an existing file."""

Expand Down Expand Up @@ -516,17 +503,6 @@ def make_temp_file(self, options: Optional[MakeTempFileOptions] = None) -> str:

return result

def open(self, path: str, options: Optional[FsOpenOptions] = None) -> FsFile:
"""Open a file and return a file descriptor."""

params = {"path": path}
if options is not None:
params["options"] = convert_to_camel_case(options)

result = self._rpc.call("open", params)

return result

def read_link(self, path: str) -> str:
"""Read the target of a symbolic link."""

Expand Down Expand Up @@ -727,14 +703,6 @@ async def expand_glob(

return result

async def create(self, path: str) -> AsyncFsFile:
"""Create a new empty file at the specified path."""

params = {"path": path}
result = await self._rpc.call("create", params)

return result

async def link(self, target: str, path: str) -> None:
"""Create a hard link pointing to an existing file."""

Expand Down Expand Up @@ -774,19 +742,6 @@ async def make_temp_file(

return result

async def open(
self, path: str, options: Optional[FsOpenOptions] = None
) -> AsyncFsFile:
"""Open a file and return a file descriptor."""

params = {"path": path}
if options is not None:
params["options"] = convert_to_camel_case(options)

result = await self._rpc.call("open", params)

return result

async def read_link(self, path: str) -> str:
"""Read the target of a symbolic link."""

Expand Down
4 changes: 4 additions & 0 deletions src/deno_sandbox/api_types_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,10 @@ class ExpandGlobOptions(TypedDict):
"""Whether the glob matching should be case insensitive. Default: false."""


class FsFileHandle(TypedDict):
file_handle_id: int


class MakeTempDirOptions(TypedDict):
dir: NotRequired[str | None]
prefix: NotRequired[str | None]
Expand Down
75 changes: 72 additions & 3 deletions src/deno_sandbox/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@

from .api_generated import (
AsyncSandboxEnv,
AsyncSandboxFs,
AsyncSandboxFs as AsyncSandboxFsGenerated,
SandboxEnv,
SandboxFs,
SandboxFs as SandboxFsGenerated,
)
from .api_types_generated import (
DenoReplOptions,
DenoRunOptions,
FsFileHandle,
FsOpenOptions,
SandboxListOptions,
SandboxCreateOptions,
SandboxConnectOptions,
Expand All @@ -39,16 +41,23 @@
from .transport import (
WebSocketTransport,
)
from .utils import to_camel_case, to_snake_case
from .utils import (
convert_to_camel_case,
convert_to_snake_case,
to_camel_case,
to_snake_case,
)
from .wrappers import (
AsyncChildProcess,
AsyncDenoProcess,
AsyncDenoRepl,
AsyncFetchResponse,
AsyncFsFile,
ChildProcess,
DenoProcess,
DenoRepl,
FetchResponse,
FsFile,
ProcessSpawnResult,
RemoteProcessOptions,
)
Expand Down Expand Up @@ -562,6 +571,66 @@ def __exit__(self, exc_type, exc_val, exc_tb):
self._client._bridge.run(self._async.__aexit__(exc_type, exc_val, exc_tb))


class AsyncSandboxFs(AsyncSandboxFsGenerated):
"""Filesystem operations inside the sandbox."""

async def create(self, path: str) -> AsyncFsFile:
"""Create a new, empty file at the specified path."""

params = {"path": path}
result = await self._rpc.call("create", params)

raw_result = convert_to_snake_case(result)
handle = cast(FsFileHandle, raw_result)

return AsyncFsFile(self._rpc, handle["file_handle_id"])

async def open(
self, path: str, options: Optional[FsOpenOptions] = None
) -> AsyncFsFile:
"""Open a file and return a file descriptor."""

params = {"path": path}
if options is not None:
params["options"] = convert_to_camel_case(options)

result = await self._rpc.call("open", params)

raw_result = convert_to_snake_case(result)
handle = cast(FsFileHandle, raw_result)

return AsyncFsFile(self._rpc, handle["file_handle_id"])


class SandboxFs(SandboxFsGenerated):
"""Filesystem operations inside the sandbox."""

def create(self, path: str) -> FsFile:
"""Create a new, empty file at the specified path."""

params = {"path": path}
result = self._rpc.call("create", params)

raw_result = convert_to_snake_case(result)
handle = cast(FsFileHandle, raw_result)

return FsFile(self._rpc, handle["file_handle_id"])

def open(self, path: str, options: Optional[FsOpenOptions] = None) -> FsFile:
"""Open a file and return a file descriptor."""

params = {"path": path}
if options is not None:
params["options"] = convert_to_camel_case(options)

result = self._rpc.call("open", params)

raw_result = convert_to_snake_case(result)
handle = cast(FsFileHandle, raw_result)

return FsFile(self._rpc, handle["file_handle_id"])


class AsyncVsCode:
"""Experimental! A VSCode instance running inside the sandbox."""

Expand Down
26 changes: 14 additions & 12 deletions src/deno_sandbox/wrappers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import base64
import sys
from typing import Any, BinaryIO, Callable, Optional, TypedDict, cast
from typing_extensions import Literal
Expand Down Expand Up @@ -49,9 +50,10 @@ async def write(self, data: bytes) -> int:
"""Write data to the file. Returns number of bytes written."""

result = await self._rpc.call(
"fileWrite", {"data": data, "fileHandleId": self._fd}
"fileWrite",
{"data": base64.b64encode(data).decode("ascii"), "fileHandleId": self._fd},
)
return result["bytesWritten"]
return result["bytes_written"]

async def truncate(self, size: Optional[int]) -> None:
"""Truncate the file to the given size. If size is None, truncate to 0."""
Expand All @@ -64,7 +66,7 @@ async def read(self, size: int) -> bytes:
result = await self._rpc.call(
"fileRead", {"length": size, "fileHandleId": self._fd}
)
return result
return base64.b64decode(result["data"])

async def seek(self, offset: int, whence: int) -> int:
"""Seek to a position in the file. Returns the new position."""
Expand All @@ -80,12 +82,12 @@ async def stat(self) -> FileInfo:
result = await self._rpc.call("fileStat", {"fileHandleId": self._fd})
return cast(FileInfo, result)

async def flush(self) -> None:
"""Flush the file's internal buffer."""
async def sync(self) -> None:
"""Flushes any pending data and metadata operations of the given file stream to disk."""

await self._rpc.call("fileFlush", {"fileHandleId": self._fd})
await self._rpc.call("fileSync", {"fileHandleId": self._fd})

async def syncData(self) -> None:
async def sync_data(self) -> None:
"""Sync the file's data to disk."""

await self._rpc.call("fileSyncData", {"fileHandleId": self._fd})
Expand Down Expand Up @@ -152,15 +154,15 @@ def stat(self) -> FileInfo:

return self._rpc._bridge.run(self._async.stat())

def flush(self) -> None:
"""Flush the file's internal buffer."""
def sync(self) -> None:
"""Flushes any pending data and metadata operations of the given file stream to disk."""

return self._rpc._bridge.run(self._async.flush())
return self._rpc._bridge.run(self._async.sync())

def syncData(self) -> None:
def sync_data(self) -> None:
"""Sync the file's data to disk."""

return self._rpc._bridge.run(self._async.syncData())
return self._rpc._bridge.run(self._async.sync_data())

def utime(self, atime: int, mtime: int) -> None:
"""Update the file's access and modification times."""
Expand Down
Loading