SafeCmd.run executes curated commands asynchronously with predictable capture
and echo semantics and returns a structured CommandResult:
stdoutandstderrare captured by default. Setcapture=Falseto stream only; the result will carryNonefor output fields.echo=Truetees stdout/stderr to the parent process while still capturing them whencapture=True.- Pass an
ExecutionContextvia thecontextparameter to override execution details: envoverlays key/value pairs on top of the current environment without mutatingos.environ; use it to pass per-command settings.cwdsets the working directory for the subprocess when provided.cancel_gracecontrols how long Cuprum waits afterSIGTERM(termination signal) before escalating toSIGKILL(kill signal).stdout_sinkandstderr_sinkroute echoed output to alternative text streams whenecho=True.encodinganderrorsconfigure how captured output is decoded; defaults are"utf-8"with"replace".exit_code,pid, andokon theCommandResultmake it easy to branch on success.
from cuprum import ECHO, ExecutionContext, sh
async def greet() -> None:
cmd = sh.make(ECHO)("-n", "hello runtime")
ctx = ExecutionContext(env={"GREETING": "1"})
result = await cmd.run(echo=True, context=ctx)
if not result.ok:
raise RuntimeError(f"echo failed: {result.exit_code}")
print(result.stdout)
If the awaiting task is cancelled while a command is running, Cuprum sends
SIGTERM to the subprocess, waits for a short grace period, and then escalates
to SIGKILL to ensure the child process is cleaned up.
Synchronous execution
For scripts or contexts where async/await is not available, use run_sync():
from cuprum import ECHO, ExecutionContext, sh
def greet() -> None:
cmd = sh.make(ECHO)("-n", "hello sync")
ctx = ExecutionContext(env={"GREETING": "1"})
result = cmd.run_sync(echo=True, context=ctx)
if not result.ok:
raise RuntimeError(f"echo failed: {result.exit_code}")
print(result.stdout)
run_sync() accepts the same parameters as run() and returns an identical
CommandResult. It drives the event loop internally via asyncio.run().