fix: Read file-like KVS values before upload and reject unencodable ones (#965)
Passing a file-like value (`open(...)`, `io.BytesIO(...)`) to
`set_record` — or to `actor.start` / `actor.call` /
`actor.validate_input` / `run.metamorph` via `run_input` — crashed with
a raw `TypeError: 'BytesIO' object is not an instance of 'Sequence'`.
`encode_key_value_store_record_value` set a content type for `io.IOBase`
but passed the object through unread, and impit's `content=` only
accepts bytes-like bodies.
The encoder now reads file-like values into memory. Detection is
duck-typed on a callable `read`, so non-`io.IOBase` wrappers work too.
Binary maps to `application/octet-stream`, text to `text/plain`.
`read()` is called with no arguments, so the value is consumed from its
current position and the object is neither rewound nor closed.
Values it cannot turn into a body now raise `TypeError` instead of
reaching the transport:
- async file-likes (`aiofiles`, Starlette `UploadFile`) — `read()`
returns a coroutine
- `read()` returning neither bytes nor `str`, e.g. `None` from a
non-blocking raw stream
- a non-bytes-like value with a non-JSON content type, e.g.
`set_record('k', {'a': 1}, content_type='image/png')`
Note: impit exposes no streaming `content=` API, so uploads are fully
buffered rather than streamed.
Tests: `set_record` over `BytesIO` / `StringIO` / a duck-typed reader,
sync and async, over both compression algorithms, plus encoder coverage
of the three rejection paths.
*✍️ Drafted by Claude Code* V
Vlada Dusek committed
064b5cccbce8b585f58271b089e4987c6559a7d8
Parent: ac95e35
Committed by GitHub <noreply@github.com>
on 8/3/2026, 10:17:59 AM