feat(coding-agent/ipy): added command tracking and exit code handling to ShellResult

- Added `args` field to ShellResult to capture the executed command.
- Added `exit_code` property to ShellResult as an alias for `returncode`.
- Added `check_returncode()` method to ShellResult to raise CalledProcessError on non-zero exit codes.
- Renamed `code` field to `returncode` in ShellResult with backward-compatible `code` property.
- Updated `run()` command documentation to clarify available ShellResult fields.
This commit is contained in:
can1357
2026-03-17 19:26:28 +01:00
parent ca332be917
commit f8b73cc3d7
2 changed files with 35 additions and 8 deletions
+10
View File
@@ -1,6 +1,16 @@
# Changelog
## [Unreleased]
### Added
- Added `args` field to ShellResult to capture the executed command
- Added `exit_code` property to ShellResult as an alias for `returncode`
- Added `check_returncode()` method to ShellResult to raise CalledProcessError on non-zero exit codes
### Changed
- Renamed `code` field to `returncode` in ShellResult (accessible via `code` property for backward compatibility)
- Updated `run()` command documentation to clarify available ShellResult fields
## [13.12.9] - 2026-03-17
### Added
+25 -8
View File
@@ -305,23 +305,40 @@ if "__omp_prelude_loaded__" not in globals():
class ShellResult:
"""Result from shell command execution."""
__slots__ = ("stdout", "stderr", "code")
def __init__(self, stdout: str, stderr: str, code: int):
__slots__ = ("args", "stdout", "stderr", "returncode")
def __init__(self, args: str, stdout: str, stderr: str, returncode: int):
self.args = args
self.stdout = stdout
self.stderr = stderr
self.code = code
self.returncode = returncode
@property
def code(self) -> int:
return self.returncode
@property
def exit_code(self) -> int:
return self.returncode
def check_returncode(self) -> None:
if self.returncode != 0:
raise subprocess.CalledProcessError(
self.returncode, self.args, output=self.stdout, stderr=self.stderr
)
def __repr__(self):
if self.code == 0:
if self.returncode == 0:
return ""
return f"exit code {self.code}"
return f"exit code {self.returncode}"
def __bool__(self):
return self.code == 0
return self.returncode == 0
def _make_shell_result(proc: subprocess.CompletedProcess[str], cmd: str) -> ShellResult:
"""Create ShellResult and emit status."""
output = proc.stdout + proc.stderr if proc.stderr else proc.stdout
_emit_status("sh", cmd=cmd[:80], code=proc.returncode, output=output[:500])
return ShellResult(proc.stdout, proc.stderr, proc.returncode)
return ShellResult(cmd, proc.stdout, proc.stderr, proc.returncode)
import signal as _signal
@@ -356,7 +373,7 @@ if "__omp_prelude_loaded__" not in globals():
@_category("Shell")
def run(cmd: str, *, cwd: str | Path | None = None, timeout: int | None = None) -> ShellResult:
"""Run a shell command."""
"""Run a shell command. Returns ShellResult with stdout/stderr and returncode/exit_code fields."""
shell_path = shutil.which("bash") or shutil.which("sh") or "/bin/sh"
args = [shell_path, "-c", cmd]
return _run_with_interrupt(args, str(cwd) if cwd else None, timeout, cmd)