Skip to content

Commit 56c401e

Browse files
Rohitpro1romanlutz
andauthored
FIX Handle Ctrl+C cancellation in CLI scenario polling (#3747) (#3813)
Co-authored-by: Roman Lutz <romanlutz13@gmail.com>
1 parent 765e200 commit 56c401e

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

‎pyrit/cli/pyrit_scan.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,11 +1112,12 @@ async def _run_scenario_async(
11121112
client=client,
11131113
scenario_result_id=scenario_result_id,
11141114
)
1115-
except KeyboardInterrupt:
1115+
except (KeyboardInterrupt, asyncio.CancelledError):
11161116
print("\n\nCancelling scenario run...")
1117+
cancellation_task = asyncio.create_task(client.cancel_scenario_run_async(scenario_result_id=scenario_result_id))
11171118
try:
1118-
await client.cancel_scenario_run_async(scenario_result_id=scenario_result_id)
1119-
print("Scenario run cancelled.")
1119+
await asyncio.shield(cancellation_task)
1120+
print("Cancellation request sent to server.")
11201121
except Exception:
11211122
print("Warning: could not cancel scenario run on server.")
11221123
return 1

‎tests/unit/cli/test_pyrit_scan.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Unit tests for the pyrit_scan CLI module (thin REST client).
66
"""
77

8+
import asyncio
89
import logging
910
from argparse import Namespace
1011
from datetime import UTC
@@ -89,6 +90,20 @@ def test_compatibility_failure_exits_without_replay(stage, capsys):
8990
assert "server run may still be active" in output
9091

9192

93+
def test_run_scenario_cancellation_requests_server_cancellation(capsys):
94+
client = _mock_api_client()
95+
with (
96+
patch("pyrit.cli.api_client.PyRITApiClient", return_value=client),
97+
patch("pyrit.cli._server_launcher.ServerLauncher.probe_health_async", new=AsyncMock(return_value=True)),
98+
patch.object(pyrit_scan, "_poll_until_terminal_async", new=AsyncMock(side_effect=asyncio.CancelledError)),
99+
):
100+
exit_code = pyrit_scan.main(["run", "test_scenario", "--target", "t"])
101+
102+
assert exit_code == 1
103+
client.cancel_scenario_run_async.assert_awaited_once_with(scenario_result_id="test-id-123")
104+
assert "Cancellation request sent to server." in capsys.readouterr().out
105+
106+
92107
def test_dataset_filter_help_covers_every_request_model_key():
93108
"""
94109
The frontend per-key ``--dataset-filters`` help must describe exactly the server-side allow-list.

0 commit comments

Comments
 (0)