Skip to content

Commit 526a714

Browse files
committed
Handle ABIDES validation timeouts
1 parent 3e65754 commit 526a714

2 files changed

Lines changed: 48 additions & 22 deletions

File tree

codeclash/arenas/abides/abides.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,32 @@ def validate_code(self, agent: Player) -> tuple[bool, str | None]:
5555
if syntax_check["returncode"] != 0:
5656
return False, f"Python syntax error in `{self.submission}`:\n{syntax_check['output']}"
5757

58-
import_check = agent.environment.execute(
59-
"python - <<'PY'\n"
60-
"import importlib.util\n"
61-
f"spec = importlib.util.spec_from_file_location('submission_agent', {self.submission!r})\n"
62-
"module = importlib.util.module_from_spec(spec)\n"
63-
"spec.loader.exec_module(module)\n"
64-
"assert hasattr(module, 'decide'), 'decide function not found'\n"
65-
"assert callable(module.decide), 'decide must be callable'\n"
66-
"observation = {\n"
67-
" 'symbol': 'JPM',\n"
68-
" 'cash': 10000000,\n"
69-
" 'position': 0,\n"
70-
" 'best_bid': None,\n"
71-
" 'best_ask': None,\n"
72-
" 'last_trade': 100000,\n"
73-
" 'market_open': True,\n"
74-
"}\n"
75-
"result = module.decide(observation)\n"
76-
"assert result is None or isinstance(result, (list, tuple, dict)), 'decide must return a list, tuple, dict, or None'\n"
77-
"PY",
78-
timeout=int(self._game_arg("validation_timeout")),
79-
)
58+
validation_timeout = int(self._game_arg("validation_timeout"))
59+
try:
60+
import_check = agent.environment.execute(
61+
"python - <<'PY'\n"
62+
"import importlib.util\n"
63+
f"spec = importlib.util.spec_from_file_location('submission_agent', {self.submission!r})\n"
64+
"module = importlib.util.module_from_spec(spec)\n"
65+
"spec.loader.exec_module(module)\n"
66+
"assert hasattr(module, 'decide'), 'decide function not found'\n"
67+
"assert callable(module.decide), 'decide must be callable'\n"
68+
"observation = {\n"
69+
" 'symbol': 'JPM',\n"
70+
" 'cash': 10000000,\n"
71+
" 'position': 0,\n"
72+
" 'best_bid': None,\n"
73+
" 'best_ask': None,\n"
74+
" 'last_trade': 100000,\n"
75+
" 'market_open': True,\n"
76+
"}\n"
77+
"result = module.decide(observation)\n"
78+
"assert result is None or isinstance(result, (list, tuple, dict)), 'decide must return a list, tuple, dict, or None'\n"
79+
"PY",
80+
timeout=validation_timeout,
81+
)
82+
except subprocess.TimeoutExpired:
83+
return False, f"`decide` validation exceeded {validation_timeout}s timeout"
8084
if import_check["returncode"] != 0:
8185
return (
8286
False,

tests/arenas/test_abides.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import subprocess
23
from pathlib import Path
34

45
from codeclash.arenas.abides.abides import CRASH_SCORE, ABIDESArena
@@ -109,6 +110,27 @@ def test_import_failure(self, mock_player_factory):
109110
assert valid is False
110111
assert "Could not import or call" in error
111112

113+
def test_validation_timeout_invalidates_submission(self):
114+
arena = ABIDESArena.__new__(ABIDESArena)
115+
arena.submission = "abides_agent.py"
116+
arena.config = {"game": {"args": {"validation_timeout": 5}}}
117+
118+
class TimeoutEnvironment(MockEnvironment):
119+
def execute(self, cmd, cwd=None, timeout=None):
120+
if cmd.startswith("python - <<'PY'"):
121+
raise subprocess.TimeoutExpired(cmd=cmd, timeout=timeout)
122+
return super().execute(cmd, cwd=cwd, timeout=timeout)
123+
124+
player = MockPlayer(
125+
"Alice",
126+
TimeoutEnvironment(files={"abides_agent.py": "def decide(observation):\n return []\n"}),
127+
)
128+
129+
valid, error = arena.validate_code(player)
130+
131+
assert valid is False
132+
assert "`decide` validation exceeded 5s timeout" in error
133+
112134

113135
class TestABIDESResults:
114136
def test_parse_winner(self, tmp_log_dir):

0 commit comments

Comments
 (0)