Skip to content
Open
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
14 changes: 14 additions & 0 deletions src/wokwi_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
upload,
upload_file,
upload_idf_firmware,
upload_text,
)
from .framebuffer import (
read_framebuffer_png_bytes,
Expand Down Expand Up @@ -90,6 +91,19 @@ async def upload(self, name: str, content: bytes) -> None:
"""
await upload(self._transport, name, content)

async def upload_text(self, name: str, content: str) -> None:
"""
Upload a text file to the simulator from a string.

Use this method for text-based files such as JSON chip definitions
(``*.chip.json``). For binary files, use :meth:`upload`.

Args:
name: The name to use for the uploaded file.
content: The file content as a string.
"""
await upload_text(self._transport, name, content)

async def upload_file(self, filename: str, local_path: Optional[Path] = None) -> str:
"""
Upload a local file to the simulator.
Expand Down
1 change: 1 addition & 0 deletions src/wokwi_client/client_sync.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class WokwiClientSync:

# Methods mirrored from WokwiClient (sync variants)
def upload(self, name: str, content: bytes) -> None: ...
def upload_text(self, name: str, content: str) -> None: ...
def upload_file(self, filename: str, local_path: Path | None = None) -> str: ...
def upload_idf_firmware(self, flasher_args_path: str | Path) -> IdfFirmwareUploadResult: ...
def download(self, name: str) -> bytes: ...
Expand Down
20 changes: 17 additions & 3 deletions src/wokwi_client/file_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

from wokwi_client.idf import resolveIdfFirmware

from .models import UploadParams
from .models import TextUploadParams, UploadParams
from .protocol_types import ResponseMessage
from .transport import Transport

_TEXT_EXTENSIONS = {".json", ".toml", ".txt", ".yaml", ".yml"}


class FlashSection:
"""A flash section with its offset and remote file name."""
Expand All @@ -33,8 +35,15 @@ async def upload_file(
transport: Transport, filename: str, local_path: Optional[Path] = None
) -> str:
firmware_path = local_path or filename
content = Path(firmware_path).read_bytes()
await upload(transport, filename, content)
file_path = Path(firmware_path)
# Use the remote filename's extension to determine the upload format,
# since the server's interpretation is based on the destination name.
if Path(filename).suffix in _TEXT_EXTENSIONS:
text_content = file_path.read_text(encoding="utf-8")
await upload_text(transport, filename, text_content)
else:
content = file_path.read_bytes()
await upload(transport, filename, content)
return filename


Expand All @@ -55,5 +64,10 @@ async def upload(transport: Transport, name: str, content: bytes) -> ResponseMes
return await transport.request("file:upload", params.model_dump())


async def upload_text(transport: Transport, name: str, content: str) -> ResponseMessage:
params = TextUploadParams(name=name, text=content)
return await transport.request("file:upload", params.model_dump())


async def download(transport: Transport, name: str) -> ResponseMessage:
return await transport.request("file:download", {"name": name})
5 changes: 5 additions & 0 deletions src/wokwi_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ class UploadParams(BaseModel):
binary: str # base64


class TextUploadParams(BaseModel):
name: str
text: str


class SimulationParams(BaseModel):
firmware: str
elf: str
Expand Down