Skip to content

Commit 42c9d30

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: move format_duration to _output module to reduce coupling
The format_duration utility was defined in engine.py but is a pure formatting function unrelated to the engine's run-loop logic. This forced _console_emitter.py to import from engine.py, creating an unnecessary dependency between the rendering layer and the engine. Moved to _output.py where formatting utilities already live, and consolidated duplicate tests (previously in both test_cli.py and test_engine.py) into test_output.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c8abf16 commit 42c9d30

6 files changed

Lines changed: 34 additions & 51 deletions

File tree

src/ralphify/_console_emitter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from rich.console import Console
1414

1515
from ralphify._events import Event, EventType
16-
from ralphify.engine import format_duration
16+
from ralphify._output import format_duration
1717

1818

1919
class ConsoleEmitter:

src/ralphify/_output.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Combine and truncate subprocess output for prompt injection.
1+
"""Combine, truncate, and format subprocess output.
22
33
Output from checks and contexts is capped at :data:`MAX_OUTPUT_LEN`
44
characters (5 000) to avoid blowing up the agent's context window.
@@ -30,3 +30,16 @@ def truncate_output(text: str, max_len: int = MAX_OUTPUT_LEN) -> str:
3030
if len(text) > max_len:
3131
return text[:max_len] + "\n... (truncated)"
3232
return text
33+
34+
35+
def format_duration(seconds: float) -> str:
36+
"""Format duration in human-readable form."""
37+
if seconds < 60:
38+
return f"{seconds:.1f}s"
39+
minutes = int(seconds // 60)
40+
secs = seconds % 60
41+
if minutes < 60:
42+
return f"{minutes}m {secs:.0f}s"
43+
hours = minutes // 60
44+
mins = minutes % 60
45+
return f"{hours}h {mins}m"

src/ralphify/engine.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from pathlib import Path
1818
from typing import NamedTuple
1919
from ralphify._events import Event, EventEmitter, EventType, NullEmitter
20-
from ralphify._output import collect_output
20+
from ralphify._output import collect_output, format_duration
2121
from ralphify.checks import (
2222
Check,
2323
discover_checks,
@@ -123,19 +123,6 @@ def consume_reload_request(self) -> bool:
123123
return False
124124

125125

126-
def format_duration(seconds: float) -> str:
127-
"""Format duration in human-readable form."""
128-
if seconds < 60:
129-
return f"{seconds:.1f}s"
130-
minutes = int(seconds // 60)
131-
secs = seconds % 60
132-
if minutes < 60:
133-
return f"{minutes}m {secs:.0f}s"
134-
hours = minutes // 60
135-
mins = minutes % 60
136-
return f"{hours}h {mins}m"
137-
138-
139126
def _write_log(
140127
log_path_dir: Path,
141128
iteration: int,

tests/test_cli.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from ralphify.contexts import Context, ContextResult
1111
from ralphify.cli import app, CONFIG_FILENAME
1212
from ralphify._templates import RALPH_TOML_TEMPLATE, PROMPT_TEMPLATE
13-
from ralphify.engine import format_duration
1413

1514
runner = CliRunner()
1615

@@ -529,22 +528,6 @@ def test_timeout_shows_in_header(self, mock_run, tmp_path, monkeypatch):
529528
assert "5m 0s per iteration" in result.output
530529

531530

532-
class TestFormatDuration:
533-
def test_seconds(self):
534-
assert format_duration(5.3) == "5.3s"
535-
assert format_duration(0.1) == "0.1s"
536-
assert format_duration(59.9) == "59.9s"
537-
538-
def test_minutes(self):
539-
assert format_duration(60) == "1m 0s"
540-
assert format_duration(90.5) == "1m 30s"
541-
assert format_duration(3599) == "59m 59s"
542-
543-
def test_hours(self):
544-
assert format_duration(3600) == "1h 0m"
545-
assert format_duration(5400) == "1h 30m"
546-
547-
548531
def _setup_check(tmp_path, name="ruff-lint", command="ruff check .", enabled=True,
549532
body="Fix lint errors."):
550533
"""Helper to create a check directory with CHECK.md."""

tests/test_engine.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from unittest.mock import patch
88

99
from ralphify._events import EventType, NullEmitter, QueueEmitter
10-
from ralphify.engine import RunConfig, RunState, RunStatus, format_duration, run_loop
10+
from ralphify.engine import RunConfig, RunState, RunStatus, run_loop
1111

1212
_MOCK_SUBPROCESS = "ralphify.engine.subprocess.run"
1313

@@ -278,19 +278,3 @@ def stop_later():
278278

279279
assert state.status == RunStatus.STOPPED
280280
assert mock_run.call_count == 1
281-
282-
283-
class TestFormatDuration:
284-
def test_seconds(self):
285-
assert format_duration(5.3) == "5.3s"
286-
assert format_duration(0.1) == "0.1s"
287-
assert format_duration(59.9) == "59.9s"
288-
289-
def test_minutes(self):
290-
assert format_duration(60) == "1m 0s"
291-
assert format_duration(90.5) == "1m 30s"
292-
assert format_duration(3599) == "59m 59s"
293-
294-
def test_hours(self):
295-
assert format_duration(3600) == "1h 0m"
296-
assert format_duration(5400) == "1h 30m"

tests/test_output.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ralphify._output import MAX_OUTPUT_LEN, collect_output, truncate_output
1+
from ralphify._output import MAX_OUTPUT_LEN, collect_output, format_duration, truncate_output
22

33

44
class TestCollectOutput:
@@ -61,3 +61,19 @@ def test_over_limit_truncated(self):
6161
def test_custom_max_len(self):
6262
result = truncate_output("hello world", max_len=5)
6363
assert result == "hello\n... (truncated)"
64+
65+
66+
class TestFormatDuration:
67+
def test_seconds(self):
68+
assert format_duration(5.3) == "5.3s"
69+
assert format_duration(0.1) == "0.1s"
70+
assert format_duration(59.9) == "59.9s"
71+
72+
def test_minutes(self):
73+
assert format_duration(60) == "1m 0s"
74+
assert format_duration(90.5) == "1m 30s"
75+
assert format_duration(3599) == "59m 59s"
76+
77+
def test_hours(self):
78+
assert format_duration(3600) == "1h 0m"
79+
assert format_duration(5400) == "1h 30m"

0 commit comments

Comments
 (0)