Non-numeric input crashes with a traceback

Done

Devanshi-cmd/devswarm-demo#5 · 2026-11-09 18:05:41 UTC · took 41.7s · Pull request

  1. Plan (done)
  2. Code (done)
  3. Test (done)
  4. Review (done)
  5. Finish (done)
Cost
$0.0054
Tests
Passed
Iterations
1
Re-plans
0
LLM calls
10
Prompt read from cache
52%

Usage by agent

Cost per agent
planner: $0.0011 planner planner, Cost: $0.0011 $0.0011 coder: $0.0015 coder coder, Cost: $0.0015 $0.0015 tester: $0.0019 tester tester, Cost: $0.0019 $0.0019 reviewer: $0.0010 reviewer reviewer, Cost: $0.0010 $0.0010
Tokens per agent
  • Cache read
  • Cache write
  • Input
  • Output
planner: 8,692 planner planner, Cache read: 4,445 planner, Cache write: 2,953 planner, Output: 1,286 8,692 coder: 14.7K coder coder, Cache read: 7,668 coder, Cache write: 5,564 coder, Output: 1,479 14.7K tester: 18.6K tester tester, Cache read: 9,973 tester, Cache write: 6,858 tester, Output: 1,792 18.6K reviewer: 5,478 reviewer reviewer, Cache write: 4,658 reviewer, Output: 816 5,478
View as table
AgentCostCache readCache writeInputOutput
planner$0.00114,4452,95381,286
coder$0.00157,6685,56481,479
tester$0.00199,9736,85881,792
reviewer$0.001004,6584816

Timeline

  1. planner iteration 1 Finished 3 calls · 8,692 tokens · $0.0011 · 8.3s

    Loading…

  2. coder iteration 1 Finished 3 calls · 14.7K tokens · $0.0015 · 8.9s

    Loading…

  3. tester iteration 1 Finished 3 calls · 18.6K tokens · $0.0019 · 11.9s

    Loading…

  4. verify iteration 1 Passed 0.6s
    ...............                                                          [100%]
    15 passed in 0.14s
    
  5. reviewer iteration 1 Finished 1 calls · 5,478 tokens · $0.0010 · 4.7s

    Loading…

Final diff

diff --git a/calc.py b/calc.py
index f16c6ca..8f65aeb 100644
--- a/calc.py
+++ b/calc.py
@@ -7,6 +7,13 @@ class CalculatorError(Exception):
     """A problem with the user's input, reported without a traceback."""
 
 
+def parse_number(text: str) -> float:
+    try:
+        return float(text)
+    except ValueError:
+        raise CalculatorError(f"invalid number: {text}") from None
+
+
 def calculate(left: float, operator: str, right: float) -> float:
     if operator == "+":
         return left + right
@@ -24,7 +31,7 @@ def main(argv: list[str]) -> int:
         print("usage: calc.py <number> <operator> <number>", file=sys.stderr)
         return 2
     try:
-        result = calculate(float(argv[0]), argv[1], float(argv[2]))
+        result = calculate(parse_number(argv[0]), argv[1], parse_number(argv[2]))
     except CalculatorError as error:
         print(f"error: {error}", file=sys.stderr)
         return 1
diff --git a/test_calc.py b/test_calc.py
index 856b0c9..d765497 100644
--- a/test_calc.py
+++ b/test_calc.py
@@ -1,5 +1,5 @@
 import pytest
-from calc import CalculatorError, calculate, main
+from calc import CalculatorError, calculate, main, parse_number
 
 
 @pytest.mark.parametrize(
@@ -23,3 +23,75 @@ def test_main_prints_result(capsys):
 def test_main_reports_unknown_operator(capsys):
     assert main(["1", "%", "2"]) == 1
     assert "unknown operator" in capsys.readouterr().err
+
+
+def test_parse_number():
+    assert parse_number("2.5") == 2.5
+    with pytest.raises(CalculatorError):
+        parse_number("abc")
+
+
+def test_main_reports_invalid_left_number(capsys):
+    assert main(["abc", "+", "2"]) == 1
+    captured = capsys.readouterr()
+    assert captured.out == ""
+    assert captured.err.strip() == "error: invalid number: abc"
+
+
+def test_main_reports_invalid_right_number(capsys):
+    assert main(["2", "+", "xyz"]) == 1
+    captured = capsys.readouterr()
+    assert captured.out == ""
+    assert captured.err.strip() == "error: invalid number: xyz"
+
+
+def test_parse_number_error_uses_original_text():
+    with pytest.raises(CalculatorError) as info:
+        parse_number("1_x")
+    assert str(info.value) == "invalid number: 1_x"
+
+
+def test_parse_number_accepts_nan_and_inf():
+    import math
+
+    assert math.isnan(parse_number("nan"))
+    assert parse_number("inf") == float("inf")
+    assert parse_number("1_000") == 1000.0
+
+
+@pytest.mark.parametrize(
+    ("argv", "bad"),
+    [(["abc", "+", "2"], "abc"), (["2", "+", "xyz"], "xyz")],
+)
+def test_cli_invalid_number_exits_1_without_traceback(argv, bad):
+    import subprocess
+    import sys
+    from pathlib import Path
+
+    script = Path(__file__).resolve().parent / "calc.py"
+    proc = subprocess.run(
+        [sys.executable, str(script), *argv],
+        capture_output=True,
+        text=True,
+        timeout=30,
+    )
+    assert proc.returncode == 1
+    assert proc.stdout == ""
+    assert proc.stderr.strip() == f"error: invalid number: {bad}"
+    assert "Traceback" not in proc.stderr
+
+
+def test_cli_valid_input_still_works():
+    import subprocess
+    import sys
+    from pathlib import Path
+
+    script = Path(__file__).resolve().parent / "calc.py"
+    proc = subprocess.run(
+        [sys.executable, str(script), "8", "/", "2"],
+        capture_output=True,
+        text=True,
+        timeout=30,
+    )
+    assert proc.returncode == 0
+    assert proc.stdout.strip() == "4.0"