runner.py python
67 lines 1.8 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago
1 """Injectable command runner for VCS subprocess calls."""
2
3 from __future__ import annotations
4
5 import shlex
6 import subprocess
7 from dataclasses import dataclass
8 from typing import Protocol
9
10
11 @dataclass(frozen=True)
12 class CommandResult:
13 stdout: str
14 stderr: str
15 exit_code: int
16
17 @property
18 def ok(self) -> bool:
19 return self.exit_code == 0
20
21
22 class CommandRunner(Protocol):
23 """Protocol for executing shell commands (real or mocked)."""
24
25 def run(self, command: str, *, cwd: str | None = None) -> CommandResult:
26 """Execute ``command`` and return stdout/stderr/exit_code."""
27
28
29 @dataclass
30 class SubprocessRunner:
31 """Production runner using ``subprocess``."""
32
33 def run(self, command: str, *, cwd: str | None = None) -> CommandResult:
34 completed = subprocess.run(
35 command,
36 shell=True,
37 cwd=cwd,
38 capture_output=True,
39 text=True,
40 )
41 return CommandResult(
42 stdout=completed.stdout.strip(),
43 stderr=completed.stderr.strip(),
44 exit_code=completed.returncode,
45 )
46
47
48 @dataclass
49 class RecordingRunner:
50 """Test runner that records commands and returns scripted responses."""
51
52 responses: dict[str, CommandResult]
53 calls: list[tuple[str, str | None]]
54
55 def run(self, command: str, *, cwd: str | None = None) -> CommandResult:
56 self.calls.append((command, cwd))
57 if command in self.responses:
58 return self.responses[command]
59 for pattern, result in self.responses.items():
60 if pattern in command:
61 return result
62 return CommandResult(stdout="", stderr="unmocked command", exit_code=127)
63
64
65 def quote_arg(value: str) -> str:
66 """Shell-quote a single argument."""
67 return shlex.quote(value)
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 2 days ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 53 days ago