Append one message as a UTF-8 JSON line, terminated with ``\\n``. Crash-safety: a single ``os.write`` of the line ensures the writer either appends the whole line or nothing. POSIX guarantees this for sizes ≤ ``PIPE_BUF`` (≥4096 bytes on every modern Unix); for large
(self, message: Any)
| 191 | return self._path |
| 192 | |
| 193 | def append(self, message: Any) -> None: |
| 194 | """Append one message as a UTF-8 JSON line, terminated with ``\\n``. |
| 195 | |
| 196 | Crash-safety: a single ``os.write`` of the line ensures the |
| 197 | writer either appends the whole line or nothing. POSIX |
| 198 | guarantees this for sizes ≤ ``PIPE_BUF`` (≥4096 bytes on every |
| 199 | modern Unix); for larger lines the reader is tolerant of |
| 200 | partial trailing content per the JSONL format design. |
| 201 | """ |
| 202 | if self._closed or self._fd is None: |
| 203 | raise RuntimeError("TranscriptWriter is closed") |
| 204 | line = _serialize_message(message) + "\n" |
| 205 | encoded = line.encode("utf-8") |
| 206 | # ``os.write`` may short-write under specific OS conditions; |
| 207 | # loop until the whole buffer is on disk. For O_APPEND files |
| 208 | # the returned ``n`` is byte count of THIS write, so the loop |
| 209 | # is straightforward. |
| 210 | view = memoryview(encoded) |
| 211 | while view: |
| 212 | written = os.write(self._fd, view) |
| 213 | if written <= 0: |
| 214 | # Defensive: 0-byte returns shouldn't happen on regular |
| 215 | # files but the loop would spin forever otherwise. |
| 216 | raise OSError(f"transcript write returned {written}") |
| 217 | view = view[written:] |
| 218 | |
| 219 | def close(self) -> None: |
| 220 | if self._closed: |