← Files Portable ResumeARCHIVED FILE
skills/.portable-resume/runtime/portable_resume/output_write.py
8.67 KB · Oct 3, 2026 · 06:34 UTC
"""Atomic no-clobber writers for already-rendered reader output.
This module never reads source-agent stores. Callers pass fully rendered
text or bytes; the helper only places those bytes on disk (or signals
stdout).
Public contract
---------------
``write_output_text`` / ``write_output_bytes``
* ``path == "-"`` → raise :class:`OutputToStdout` (not a diagnostic).
CLI / orchestrator must catch this and write the rendered payload to
stdout instead of a file.
* empty path → :class:`~portable_resume.diagnostics.DiagnosticError`
with ``E_INVALID_INPUT``.
* destination exists and ``clobber=False`` (default) → same diagnostic.
* missing parent directory → same diagnostic.
* final path is a symlink and ``clobber=False`` → refuse (no follow).
* success → returns the absolute path written.
Atomicity
---------
Payload is written to an exclusive temp file
``.portable-resume-output-<hex>.tmp`` in the destination parent, fsynced,
then ``os.replace``d onto the final path so readers never observe a
half-written destination. Private mode ``0o600`` is best-effort (Unix).
"""
from __future__ import annotations
import os
import secrets
import stat as stat_mod
from typing import Final
from .diagnostics import DiagnosticError
_TMP_PREFIX: Final[str] = ".portable-resume-output-"
_TMP_SUFFIX: Final[str] = ".tmp"
_PRIVATE_MODE: Final[int] = 0o600
class OutputToStdout(Exception):
"""Caller should emit rendered content on stdout, not a filesystem path.
Raised when the output path is exactly ``"-"``. This is a control-flow
signal for CLI wiring, not a user-facing diagnostic failure.
"""
def write_output_text(
path: str,
text: str,
*,
clobber: bool = False,
encoding: str = "utf-8",
) -> str:
"""Write *text* atomically to *path*. Returns the absolute path written.
See module docstring for ``"-"``, no-clobber, and atomic-replace rules.
"""
# Fail closed on path policy (including ``"-"``) before encoding work.
if not isinstance(path, str) or path == "":
raise DiagnosticError.invalid()
if path == "-":
raise OutputToStdout()
if not isinstance(text, str):
raise DiagnosticError.invalid()
if not isinstance(encoding, str) or not encoding:
raise DiagnosticError.invalid()
try:
payload = text.encode(encoding)
except (LookupError, UnicodeError) as error:
raise DiagnosticError.invalid() from error
return write_output_bytes(path, payload, clobber=clobber)
def write_output_bytes(
path: str,
data: bytes | bytearray | memoryview,
*,
clobber: bool = False,
) -> str:
"""Write *data* atomically via platform backend when capable (#205)."""
from .platform_fs import get_filesystem_backend
backend = get_filesystem_backend()
if backend.capabilities.atomic_output:
return backend.atomic_replace_output(path, data, clobber=clobber)
return _write_output_bytes_impl(path, data, clobber=clobber)
def _write_output_bytes_impl(
path: str,
data: bytes | bytearray | memoryview,
*,
clobber: bool = False,
) -> str:
"""Pathname atomic write implementation (used when backend lacks atomic_output)."""
abs_path = _resolve_output_path(path, clobber=clobber)
if not isinstance(data, (bytes, bytearray, memoryview)):
raise DiagnosticError.invalid()
payload = bytes(data)
parent = os.path.dirname(abs_path)
tmp_path = _stage_output_bytes(parent, payload)
try:
_commit_staged_output(tmp_path, abs_path, clobber=clobber)
except BaseException:
# Commit unlinks on failure; if staging left a file and commit
# raised before taking ownership of cleanup, drop the temp.
try:
if os.path.lexists(tmp_path):
os.unlink(tmp_path)
except OSError:
pass
raise
return abs_path
def _resolve_output_path(path: object, *, clobber: bool) -> str:
"""Validate path policy and return an absolute destination path."""
if not isinstance(path, str) or path == "":
raise DiagnosticError.invalid()
if path == "-":
raise OutputToStdout()
abs_path = os.path.abspath(path)
parent = os.path.dirname(abs_path)
if not parent or parent == abs_path:
# Empty dirname or root-as-file (e.g. "/") is not a writable file path.
raise DiagnosticError.invalid()
if not os.path.isdir(parent):
raise DiagnosticError.invalid()
if os.path.lexists(abs_path):
try:
st = os.lstat(abs_path)
except OSError as error:
raise DiagnosticError.invalid() from error
if stat_mod.S_ISLNK(st.st_mode) and not clobber:
raise DiagnosticError.invalid()
if not clobber:
raise DiagnosticError.invalid()
# Clobber only replaces a regular file or a symlink leaf (replace
# does not follow). Directories / specials are refused.
if not (stat_mod.S_ISREG(st.st_mode) or stat_mod.S_ISLNK(st.st_mode)):
raise DiagnosticError.invalid()
return abs_path
def _stage_output_bytes(parent: str, data: bytes) -> str:
"""Write *data* to a new exclusive temp under *parent*; return temp path.
Does **not** replace any final destination. Used so crash-before-replace
leaves only a temp sibling; the final path is untouched until commit.
"""
if not isinstance(data, (bytes, bytearray)):
raise DiagnosticError.invalid()
payload = bytes(data)
if not parent or not os.path.isdir(parent):
raise DiagnosticError.invalid()
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL
cloexec = getattr(os, "O_CLOEXEC", 0)
nofollow = getattr(os, "O_NOFOLLOW", 0)
# O_BINARY is required on Windows so LF bytes are not rewritten as CRLF.
binary = getattr(os, "O_BINARY", 0)
flags |= cloexec | nofollow | binary
# Retry a few times if an exclusive name collides (extremely unlikely).
last_error: OSError | None = None
for _ in range(8):
name = f"{_TMP_PREFIX}{secrets.token_hex(8)}{_TMP_SUFFIX}"
tmp_path = os.path.join(parent, name)
try:
fd = os.open(tmp_path, flags, _PRIVATE_MODE)
except FileExistsError as error:
last_error = error
continue
except OSError as error:
raise DiagnosticError.invalid() from error
try:
try:
os.fchmod(fd, _PRIVATE_MODE)
except (AttributeError, OSError):
pass
view = memoryview(payload)
while view:
written = os.write(fd, view)
if written <= 0:
raise OSError("short write to output temp")
view = view[written:]
try:
os.fsync(fd)
except OSError:
pass
except OSError as error:
try:
os.close(fd)
except OSError:
pass
try:
os.unlink(tmp_path)
except OSError:
pass
raise DiagnosticError.invalid() from error
else:
try:
os.close(fd)
except OSError as error:
try:
os.unlink(tmp_path)
except OSError:
pass
raise DiagnosticError.invalid() from error
return tmp_path
raise DiagnosticError.invalid() from last_error
def _commit_staged_output(tmp_path: str, abs_path: str, *, clobber: bool) -> None:
"""Atomically ``os.replace`` a fully-written temp onto *abs_path*.
Re-checks no-clobber after the temp is complete to shrink the TOCTOU
window. Unlinks the temp on any failure so failed flights do not leave
durable junk when cleanup is possible.
"""
if not isinstance(tmp_path, str) or not tmp_path:
raise DiagnosticError.invalid()
if not isinstance(abs_path, str) or not abs_path:
raise DiagnosticError.invalid()
try:
if not clobber and os.path.lexists(abs_path):
raise DiagnosticError.invalid()
if clobber and os.path.lexists(abs_path):
try:
st = os.lstat(abs_path)
except OSError as error:
raise DiagnosticError.invalid() from error
if not (stat_mod.S_ISREG(st.st_mode) or stat_mod.S_ISLNK(st.st_mode)):
raise DiagnosticError.invalid()
os.replace(tmp_path, abs_path)
except DiagnosticError:
try:
os.unlink(tmp_path)
except OSError:
pass
raise
except OSError as error:
try:
os.unlink(tmp_path)
except OSError:
pass
raise DiagnosticError.invalid() from error
SHA-256: c7ef826526bb242d508ad5a8cba1cb6522c6d7954da837891bb07420c8888463