rbx.cp 0.5.46__py3-none-any.whl → 0.5.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rbx/box/checkers.py +81 -28
- rbx/box/cli.py +12 -10
- rbx/box/environment.py +1 -1
- rbx/box/naming.py +22 -0
- rbx/box/packaging/boca/extension.py +1 -0
- rbx/box/packaging/boca/packager.py +54 -11
- rbx/box/packaging/main.py +7 -0
- rbx/box/packaging/moj/packager.py +89 -19
- rbx/box/packaging/packager.py +15 -3
- rbx/box/packaging/polygon/packager.py +5 -4
- rbx/box/solutions.py +2 -2
- rbx/box/statements/build_statements.py +2 -1
- rbx/box/stresses.py +0 -1
- rbx/box/tasks.py +6 -4
- rbx/grading/judge/sandbox.py +29 -1
- rbx/grading/judge/sandboxes/isolate.py +10 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +16 -4
- rbx/grading/judge/sandboxes/timeit.py +12 -3
- rbx/grading/processing_context.py +48 -0
- rbx/grading/steps.py +25 -14
- rbx/resources/packagers/boca/checker.sh +8 -6
- rbx/resources/packagers/boca/compare.sh +48 -0
- rbx/resources/packagers/boca/interactive/c +207 -0
- rbx/resources/packagers/boca/interactive/cc +207 -0
- rbx/resources/packagers/boca/interactive/cpp +207 -0
- rbx/resources/packagers/boca/interactive/java +240 -0
- rbx/resources/packagers/boca/interactive/kt +231 -0
- rbx/resources/packagers/boca/interactive/py2 +209 -0
- rbx/resources/packagers/boca/interactive/py3 +209 -0
- rbx/resources/packagers/boca/interactor_compile.sh +45 -0
- rbx/resources/packagers/boca/run/bkp +163 -0
- rbx/resources/packagers/boca/run/c +19 -19
- rbx/resources/packagers/boca/run/cc +19 -19
- rbx/resources/packagers/boca/run/cpp +19 -19
- rbx/resources/packagers/boca/run/java +51 -51
- rbx/resources/packagers/boca/run/kt +30 -30
- rbx/resources/packagers/boca/run/py2 +42 -42
- rbx/resources/packagers/boca/run/py3 +42 -42
- rbx/resources/packagers/moj/scripts/c/compile.sh +19 -0
- rbx/resources/packagers/moj/scripts/c/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/c/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/compare.sh +32 -6
- rbx/resources/packagers/moj/scripts/cpp/compile.sh +19 -0
- rbx/resources/packagers/moj/scripts/cpp/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/cpp/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/interactor_prep.sh +14 -0
- rbx/resources/packagers/moj/scripts/interactor_run.sh +38 -0
- rbx/resources/packagers/moj/scripts/java/compile.sh +12 -0
- rbx/resources/packagers/moj/scripts/java/prep.sh +8 -0
- rbx/resources/packagers/moj/scripts/java/run.sh +17 -0
- rbx/resources/packagers/moj/scripts/py2/compile.sh +7 -0
- rbx/resources/packagers/moj/scripts/py2/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/py2/run.sh +16 -0
- rbx/resources/packagers/moj/scripts/py3/compile.sh +7 -0
- rbx/resources/packagers/moj/scripts/py3/prep.sh +5 -0
- rbx/resources/packagers/moj/scripts/py3/run.sh +16 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.48.dist-info}/METADATA +1 -1
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.48.dist-info}/RECORD +61 -33
- rbx/resources/packagers/boca/compare +0 -53
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.48.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.48.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.48.dist-info}/entry_points.txt +0 -0
rbx/box/solutions.py
CHANGED
@@ -616,7 +616,7 @@ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
|
|
616
616
|
]
|
617
617
|
|
618
618
|
|
619
|
-
def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
|
619
|
+
async def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
|
620
620
|
pkg = package.find_problem_package_or_die()
|
621
621
|
if tracked_solutions is None:
|
622
622
|
tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
|
@@ -630,7 +630,7 @@ def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
|
|
630
630
|
if str(sol.path) in tracked_solutions
|
631
631
|
]
|
632
632
|
|
633
|
-
picked = questionary.checkbox('Select solutions', choices=choices).
|
633
|
+
picked = await questionary.checkbox('Select solutions', choices=choices).ask_async()
|
634
634
|
if picked is None:
|
635
635
|
raise typer.Abort()
|
636
636
|
return picked
|
@@ -7,7 +7,7 @@ import syncer
|
|
7
7
|
import typer
|
8
8
|
|
9
9
|
from rbx import annotations, console
|
10
|
-
from rbx.box import environment, package
|
10
|
+
from rbx.box import environment, naming, package
|
11
11
|
from rbx.box.schema import Package
|
12
12
|
from rbx.box.statements.builders import (
|
13
13
|
BUILDER_LIST,
|
@@ -291,6 +291,7 @@ def build_statement(
|
|
291
291
|
output_type=output_type,
|
292
292
|
use_samples=use_samples,
|
293
293
|
is_editorial=is_editorial,
|
294
|
+
short_name=naming.get_problem_shortname(),
|
294
295
|
)
|
295
296
|
statement_path = (
|
296
297
|
package.get_build_path()
|
rbx/box/stresses.py
CHANGED
rbx/box/tasks.py
CHANGED
@@ -194,7 +194,8 @@ async def _run_communication_solution_on_testcase(
|
|
194
194
|
output_path = testcase.outputPath
|
195
195
|
else:
|
196
196
|
output_path = output_dir / testcase.inputPath.with_suffix('.out').name
|
197
|
-
|
197
|
+
solution_error_path = output_path.with_suffix('.sol.err')
|
198
|
+
interactor_error_path = output_path.with_suffix('.int.err')
|
198
199
|
log_path = output_path.with_suffix('.log')
|
199
200
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
200
201
|
|
@@ -204,7 +205,7 @@ async def _run_communication_solution_on_testcase(
|
|
204
205
|
interactor_item = CommunicationItem(
|
205
206
|
code=package.get_interactor(),
|
206
207
|
executable=DigestOrSource.create(interactor_digest),
|
207
|
-
stderr=DigestOrDest.create(
|
208
|
+
stderr=DigestOrDest.create(interactor_error_path),
|
208
209
|
extra_config=interactor_extra_config,
|
209
210
|
extra_args='interactor.in interactor.out',
|
210
211
|
inputs=[
|
@@ -230,6 +231,7 @@ async def _run_communication_solution_on_testcase(
|
|
230
231
|
solution_item = CommunicationItem(
|
231
232
|
code=solution,
|
232
233
|
executable=DigestOrSource.create(compiled_digest),
|
234
|
+
stderr=DigestOrDest.create(solution_error_path),
|
233
235
|
extra_config=extra_config,
|
234
236
|
capture=DigestOrDest.create(solution_capture_path)
|
235
237
|
if solution_capture_path
|
@@ -248,7 +250,7 @@ async def _run_communication_solution_on_testcase(
|
|
248
250
|
checker_digest,
|
249
251
|
run_log,
|
250
252
|
interactor_run_log,
|
251
|
-
|
253
|
+
interactor_error_path,
|
252
254
|
testcase,
|
253
255
|
output_path,
|
254
256
|
)
|
@@ -263,7 +265,7 @@ async def _run_communication_solution_on_testcase(
|
|
263
265
|
log=TestcaseLog(
|
264
266
|
**(run_log.model_dump() if run_log is not None else {}),
|
265
267
|
stdout_absolute_path=output_path.absolute(),
|
266
|
-
stderr_absolute_path=
|
268
|
+
stderr_absolute_path=solution_error_path.absolute(),
|
267
269
|
log_absolute_path=log_path.absolute(),
|
268
270
|
),
|
269
271
|
)
|
rbx/grading/judge/sandbox.py
CHANGED
@@ -6,6 +6,7 @@ import logging
|
|
6
6
|
import os
|
7
7
|
import pathlib
|
8
8
|
import select
|
9
|
+
import signal
|
9
10
|
import stat
|
10
11
|
import subprocess
|
11
12
|
import sys
|
@@ -14,6 +15,7 @@ from typing import IO, Any, Dict, List, Optional
|
|
14
15
|
|
15
16
|
import pydantic
|
16
17
|
|
18
|
+
from rbx.grading import processing_context
|
17
19
|
from rbx.grading.judge import cacher, storage
|
18
20
|
|
19
21
|
logger = logging.getLogger(__name__)
|
@@ -187,6 +189,7 @@ class SandboxBase(abc.ABC):
|
|
187
189
|
|
188
190
|
EXIT_SANDBOX_ERROR = 'sandbox error'
|
189
191
|
EXIT_OK = 'ok'
|
192
|
+
EXIT_TERMINATED = 'terminated'
|
190
193
|
EXIT_SIGNAL = 'signal'
|
191
194
|
EXIT_TIMEOUT = 'timeout'
|
192
195
|
EXIT_TIMEOUT_WALL = 'wall timeout'
|
@@ -225,6 +228,7 @@ class SandboxBase(abc.ABC):
|
|
225
228
|
self.cmd_file = pathlib.PosixPath('commands.log')
|
226
229
|
|
227
230
|
self.params = params or SandboxParams()
|
231
|
+
self.pid = None
|
228
232
|
|
229
233
|
# Set common environment variables.
|
230
234
|
# Specifically needed by Python, that searches the home for
|
@@ -324,6 +328,28 @@ class SandboxBase(abc.ABC):
|
|
324
328
|
"""
|
325
329
|
pass
|
326
330
|
|
331
|
+
def set_pid(self, pid: int):
|
332
|
+
processing_context.add_to_processing_context(pid)
|
333
|
+
self.pid = pid
|
334
|
+
|
335
|
+
def get_pid(self) -> Optional[int]:
|
336
|
+
"""Return the PID of the sandboxed process.
|
337
|
+
|
338
|
+
return (int|None): the PID of the sandboxed process, or None if
|
339
|
+
the sandboxed process is not running.
|
340
|
+
|
341
|
+
"""
|
342
|
+
return self.pid
|
343
|
+
|
344
|
+
@abc.abstractmethod
|
345
|
+
def get_detailed_logs(self) -> str:
|
346
|
+
"""Return the detailed logs of the sandbox.
|
347
|
+
|
348
|
+
return (string): the detailed logs of the sandbox.
|
349
|
+
|
350
|
+
"""
|
351
|
+
pass
|
352
|
+
|
327
353
|
@abc.abstractmethod
|
328
354
|
def get_human_exit_description(self) -> str:
|
329
355
|
"""Get the status of the sandbox and return a human-readable
|
@@ -667,7 +693,9 @@ class SandboxBase(abc.ABC):
|
|
667
693
|
did).
|
668
694
|
|
669
695
|
"""
|
670
|
-
|
696
|
+
# SIGTERM can be safely ignored, just in case it leaks away from
|
697
|
+
# the sandbox.
|
698
|
+
return exitcode == 0 or exitcode == -signal.SIGTERM
|
671
699
|
|
672
700
|
@abc.abstractmethod
|
673
701
|
def initialize(self):
|
@@ -420,6 +420,7 @@ class IsolateSandbox(SandboxBase):
|
|
420
420
|
return (string): the main reason why the sandbox terminated.
|
421
421
|
|
422
422
|
"""
|
423
|
+
# TODO: figure out EXIT_TERMINATED
|
423
424
|
assert self.log is not None
|
424
425
|
status_list = self.get_status_list()
|
425
426
|
if 'XX' in status_list:
|
@@ -462,6 +463,14 @@ class IsolateSandbox(SandboxBase):
|
|
462
463
|
return 'Execution failed because the return code was nonzero'
|
463
464
|
return ''
|
464
465
|
|
466
|
+
def get_detailed_logs(self) -> str:
|
467
|
+
"""Return the detailed logs of the sandbox.
|
468
|
+
|
469
|
+
return (string): the detailed logs of the sandbox.
|
470
|
+
|
471
|
+
"""
|
472
|
+
return str(self.log)
|
473
|
+
|
465
474
|
def inner_absolute_path(self, path: pathlib.Path) -> pathlib.Path:
|
466
475
|
"""Translate from a relative path inside the sandbox to an
|
467
476
|
absolute path inside the sandbox.
|
@@ -602,6 +611,7 @@ class IsolateSandbox(SandboxBase):
|
|
602
611
|
# std*** to interfere with command. Otherwise we let the
|
603
612
|
# caller handle these issues.
|
604
613
|
with popen as p:
|
614
|
+
self.set_pid(p.pid)
|
605
615
|
exitcode = self.translate_box_exitcode(
|
606
616
|
wait_without_std([p], actually_pipe_to_stdout=self.debug)[0]
|
607
617
|
)
|
@@ -57,7 +57,6 @@ class StupidSandbox(SandboxBase):
|
|
57
57
|
self.initialize()
|
58
58
|
|
59
59
|
self.exec_num = -1
|
60
|
-
self.popen = None
|
61
60
|
self.log = None
|
62
61
|
self.returncode = None
|
63
62
|
|
@@ -192,9 +191,13 @@ class StupidSandbox(SandboxBase):
|
|
192
191
|
return (string): the main reason why the sandbox terminated.
|
193
192
|
|
194
193
|
"""
|
195
|
-
if self.returncode
|
194
|
+
if self.returncode is not None and not self.translate_box_exitcode(
|
195
|
+
self.returncode
|
196
|
+
):
|
196
197
|
return self.EXIT_SANDBOX_ERROR
|
197
198
|
status_list = self.get_status_list()
|
199
|
+
if 'TE' in status_list:
|
200
|
+
return self.EXIT_TERMINATED
|
198
201
|
if 'WT' in status_list:
|
199
202
|
return self.EXIT_TIMEOUT_WALL
|
200
203
|
if 'TO' in status_list:
|
@@ -218,6 +221,9 @@ class StupidSandbox(SandboxBase):
|
|
218
221
|
assert self.log is not None
|
219
222
|
return int(self.log['exit-code'])
|
220
223
|
|
224
|
+
def get_detailed_logs(self) -> str:
|
225
|
+
return str(self.log)
|
226
|
+
|
221
227
|
def get_human_exit_description(self) -> str:
|
222
228
|
"""Get the status of the sandbox and return a human-readable
|
223
229
|
string describing it.
|
@@ -299,13 +305,19 @@ class StupidSandbox(SandboxBase):
|
|
299
305
|
+ self.get_timeit_args()
|
300
306
|
+ command
|
301
307
|
)
|
302
|
-
|
308
|
+
with subprocess.Popen(
|
303
309
|
real_command,
|
304
310
|
stdin=subprocess.PIPE,
|
305
311
|
stdout=subprocess.PIPE,
|
306
312
|
stderr=subprocess.STDOUT,
|
307
313
|
env={**os.environ, **self.params.set_env},
|
308
|
-
)
|
314
|
+
) as p:
|
315
|
+
self.set_pid(p.pid)
|
316
|
+
try:
|
317
|
+
self.returncode = p.wait()
|
318
|
+
except Exception:
|
319
|
+
p.kill()
|
320
|
+
raise
|
309
321
|
self.hydrate_logs()
|
310
322
|
return self.translate_box_exitcode(self.returncode)
|
311
323
|
|
@@ -223,6 +223,7 @@ def wait_and_finish(
|
|
223
223
|
pid: int,
|
224
224
|
options: Options,
|
225
225
|
start_time: float,
|
226
|
+
status_holder: Set[str],
|
226
227
|
alarm_msg: Optional[List[Optional[str]]] = None,
|
227
228
|
):
|
228
229
|
_, exitstatus, ru = os.wait4(pid, 0)
|
@@ -237,7 +238,7 @@ def wait_and_finish(
|
|
237
238
|
if exitcode < 0:
|
238
239
|
entries.append(f'exit-sig: {-exitcode}')
|
239
240
|
|
240
|
-
status =
|
241
|
+
status = status_holder
|
241
242
|
if exitcode > 0:
|
242
243
|
status.add('RE')
|
243
244
|
if exitcode < 0:
|
@@ -284,9 +285,11 @@ def main():
|
|
284
285
|
os.chdir(options.chdir)
|
285
286
|
set_rlimits(options)
|
286
287
|
redirect_fds(options)
|
288
|
+
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
287
289
|
os.execvp(options.argv[0], options.argv)
|
288
290
|
|
289
291
|
alarm_msg: List[Optional[str]] = [None]
|
292
|
+
status_holder: Set[str] = set()
|
290
293
|
|
291
294
|
def handle_alarm(*args, **kwargs):
|
292
295
|
nonlocal alarm_msg
|
@@ -311,15 +314,21 @@ def main():
|
|
311
314
|
|
312
315
|
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
313
316
|
|
317
|
+
def handle_sub_term(*args, **kwargs):
|
318
|
+
nonlocal status_holder
|
319
|
+
status_holder.add('TE')
|
320
|
+
os.kill(sub_pid, 9)
|
321
|
+
|
314
322
|
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
315
323
|
signal.signal(signal.SIGALRM, handle_alarm)
|
316
|
-
|
324
|
+
signal.signal(signal.SIGTERM, handle_sub_term)
|
317
325
|
|
326
|
+
wait_and_finish(sub_pid, options, start_time, status_holder, alarm_msg=alarm_msg)
|
318
327
|
# Cancel alarm before exiting to avoid surprises.
|
319
328
|
signal.setitimer(signal.ITIMER_REAL, 0)
|
320
329
|
|
321
330
|
# Exit gracefully.
|
322
|
-
sys.exit()
|
331
|
+
sys.exit(0)
|
323
332
|
|
324
333
|
|
325
334
|
if __name__ == '__main__':
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import contextlib
|
2
|
+
import os
|
3
|
+
import signal
|
4
|
+
import threading
|
5
|
+
from typing import Optional, Set
|
6
|
+
|
7
|
+
_processing_context_pids: Optional[Set[int]] = None
|
8
|
+
_lock = threading.Lock()
|
9
|
+
|
10
|
+
# Creating a processing context is not thread-safe, but adding to it is.
|
11
|
+
|
12
|
+
|
13
|
+
@contextlib.contextmanager
|
14
|
+
def new_processing_context():
|
15
|
+
global _processing_context_pids
|
16
|
+
with _lock:
|
17
|
+
old_processing_context_pids = _processing_context_pids
|
18
|
+
_processing_context_pids = set()
|
19
|
+
try:
|
20
|
+
yield
|
21
|
+
finally:
|
22
|
+
with _lock:
|
23
|
+
_processing_context_pids = old_processing_context_pids
|
24
|
+
|
25
|
+
|
26
|
+
def get_processing_context() -> Set[int]:
|
27
|
+
with _lock:
|
28
|
+
return _processing_context_pids or set()
|
29
|
+
|
30
|
+
|
31
|
+
def add_to_processing_context(pid: int):
|
32
|
+
global _processing_context_pids
|
33
|
+
with _lock:
|
34
|
+
if _processing_context_pids is None:
|
35
|
+
return
|
36
|
+
_processing_context_pids.add(pid)
|
37
|
+
|
38
|
+
|
39
|
+
def terminate_all_processes_in_context():
|
40
|
+
with _lock:
|
41
|
+
if _processing_context_pids is None:
|
42
|
+
return
|
43
|
+
for pid in _processing_context_pids:
|
44
|
+
try:
|
45
|
+
os.kill(pid, signal.SIGTERM)
|
46
|
+
except OSError:
|
47
|
+
pass
|
48
|
+
_processing_context_pids.clear()
|
rbx/grading/steps.py
CHANGED
@@ -21,6 +21,7 @@ from rich.text import Text
|
|
21
21
|
from rbx import utils
|
22
22
|
from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
|
23
23
|
from rbx.console import console
|
24
|
+
from rbx.grading import processing_context
|
24
25
|
from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
|
25
26
|
from rbx.grading.judge.storage import Storage, copyfileobj
|
26
27
|
|
@@ -197,6 +198,7 @@ class RunLog(BaseModel):
|
|
197
198
|
exitstatus: str = SandboxBase.EXIT_SANDBOX_ERROR
|
198
199
|
time: Optional[float] = 0.0
|
199
200
|
memory: Optional[int] = 0
|
201
|
+
sandbox: str = ''
|
200
202
|
warnings: bool = False
|
201
203
|
metadata: Optional[RunLogMetadata] = None
|
202
204
|
|
@@ -210,7 +212,7 @@ class RunLog(BaseModel):
|
|
210
212
|
return 'OK'
|
211
213
|
time = self.time or 0.0
|
212
214
|
memory = self.memory or 0
|
213
|
-
return f'FAILED with exit code {self.exitcode} and sandbox status {self.exitstatus} (time: {time}s, memory: {memory//(1024*1024)}MB)'
|
215
|
+
return f'FAILED with exit code {self.exitcode} and sandbox status {self.exitstatus} (time: {time}s, memory: {memory // (1024 * 1024)}MB)'
|
214
216
|
|
215
217
|
|
216
218
|
class PreprocessLog(RunLog):
|
@@ -330,7 +332,7 @@ def _expand_part(part: str, sandbox: SandboxBase) -> List[str]:
|
|
330
332
|
|
331
333
|
def _split_and_expand(command: str, sandbox: SandboxBase) -> List[str]:
|
332
334
|
res = []
|
333
|
-
parts = shlex.split(command.format(memory=sandbox.params.address_space))
|
335
|
+
parts = shlex.split(command.format(memory=sandbox.params.address_space or 2048))
|
334
336
|
for part in parts:
|
335
337
|
res.extend(_expand_part(part, sandbox))
|
336
338
|
return res
|
@@ -567,6 +569,7 @@ def compile(
|
|
567
569
|
memory=sandbox.get_memory_used(),
|
568
570
|
warnings=_check_for_compilation_warnings(sandbox, stderr_file),
|
569
571
|
log='\n'.join(std_outputs),
|
572
|
+
sandbox=sandbox.get_detailed_logs(),
|
570
573
|
)
|
571
574
|
logs.append(log)
|
572
575
|
|
@@ -594,6 +597,7 @@ async def run(
|
|
594
597
|
sandbox: SandboxBase,
|
595
598
|
artifacts: GradingArtifacts,
|
596
599
|
metadata: Optional[RunLogMetadata] = None,
|
600
|
+
kill_on_processing_context_exit: bool = False,
|
597
601
|
) -> Optional[RunLog]:
|
598
602
|
_process_input_artifacts(artifacts, sandbox)
|
599
603
|
_process_fifos(artifacts, sandbox)
|
@@ -609,6 +613,9 @@ async def run(
|
|
609
613
|
)
|
610
614
|
return None
|
611
615
|
|
616
|
+
if sandbox.get_exit_code() != 0 and kill_on_processing_context_exit:
|
617
|
+
processing_context.terminate_all_processes_in_context()
|
618
|
+
|
612
619
|
if not _process_output_artifacts(artifacts, sandbox):
|
613
620
|
return None
|
614
621
|
|
@@ -625,6 +632,7 @@ async def run(
|
|
625
632
|
time=sandbox.get_execution_time(),
|
626
633
|
memory=sandbox.get_memory_used(),
|
627
634
|
metadata=metadata,
|
635
|
+
sandbox=sandbox.get_detailed_logs(),
|
628
636
|
)
|
629
637
|
if metadata is not None and metadata.is_sanitized:
|
630
638
|
run_log.warnings = _check_for_sanitizer_warnings(
|
@@ -649,19 +657,22 @@ async def run_coordinated(
|
|
649
657
|
interactor: CoordinatedRunParams,
|
650
658
|
solution: CoordinatedRunParams,
|
651
659
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
660
|
+
with processing_context.new_processing_context():
|
661
|
+
runs = tuple(
|
662
|
+
run(
|
663
|
+
params.command,
|
664
|
+
params.params,
|
665
|
+
params.sandbox,
|
666
|
+
params.artifacts,
|
667
|
+
params.metadata,
|
668
|
+
kill_on_processing_context_exit=True,
|
669
|
+
)
|
670
|
+
for params in [interactor, solution]
|
671
|
+
)
|
672
|
+
return typing.cast(
|
673
|
+
Tuple[Optional[RunLog], Optional[RunLog]],
|
674
|
+
tuple(await asyncio.gather(*runs)),
|
659
675
|
)
|
660
|
-
for params in [interactor, solution]
|
661
|
-
)
|
662
|
-
return typing.cast(
|
663
|
-
Tuple[Optional[RunLog], Optional[RunLog]], tuple(await asyncio.gather(*runs))
|
664
|
-
)
|
665
676
|
|
666
677
|
|
667
678
|
def _normalize_checked_words(s: str) -> Tuple[str, ...]:
|
@@ -1,9 +1,10 @@
|
|
1
1
|
### START OF CHECKER COMPILATION
|
2
|
+
CDIR=$(pwd)
|
2
3
|
CHECKER_PATH="checker.cpp"
|
3
|
-
CHECKER_OUT="
|
4
|
+
CHECKER_OUT="checker.exe"
|
4
5
|
|
5
6
|
# find compiler
|
6
|
-
cc
|
7
|
+
cc=$(which g++)
|
7
8
|
[ -x "$cc" ] || cc=/usr/bin/g++
|
8
9
|
if [ ! -x "$cc" ]; then
|
9
10
|
echo "$cc not found or it's not executable"
|
@@ -12,25 +13,26 @@ fi
|
|
12
13
|
read -r -d '' TestlibContent <<"EOF"
|
13
14
|
{{testlib_content}}
|
14
15
|
EOF
|
15
|
-
|
16
|
+
|
16
17
|
read -r -d '' CheckerContent <<"EOF"
|
17
18
|
{{checker_content}}
|
18
19
|
EOF
|
19
20
|
|
20
|
-
printf "%s" "${TestlibContent}" >
|
21
|
-
printf "%s" "${CheckerContent}"
|
21
|
+
printf "%s" "${TestlibContent}" >testlib.h
|
22
|
+
printf "%s" "${CheckerContent}" >$CHECKER_PATH
|
22
23
|
|
23
24
|
checker_hash=($(md5sum $CHECKER_PATH))
|
24
25
|
checker_cache="/tmp/boca-chk-${checker_hash}"
|
25
26
|
|
26
27
|
echo "Polygon checker hash: $checker_hash"
|
28
|
+
echo "Copying polygon checker to $CDIR/$CHECKER_OUT"
|
27
29
|
if [ -f "$checker_cache" ]; then
|
28
30
|
echo "Recovering polygon checker from cache: $checker_cache"
|
29
31
|
cp "$checker_cache" $CHECKER_OUT -f
|
30
32
|
else
|
31
33
|
echo "Compiling polygon checker: $CHECKER_PATH"
|
32
34
|
$cc {{rbxFlags}} $CHECKER_PATH -o $CHECKER_OUT
|
33
|
-
|
35
|
+
|
34
36
|
if [ $? -ne 0 ]; then
|
35
37
|
echo "Checker could not be compiled"
|
36
38
|
exit 47
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
if [ ! -r "$1" -o ! -r "$2" ]; then
|
3
|
+
echo "Parameter problem"
|
4
|
+
exit 43
|
5
|
+
fi
|
6
|
+
|
7
|
+
INTERACTOREXITCODE=$(grep '^testlib exitcode' "$1" | awk '{print $NF}')
|
8
|
+
if [[ -n $INTERACTOREXITCODE ]]; then
|
9
|
+
echo "interactor exitcode = $INTERACTOREXITCODE"
|
10
|
+
if [[ $INTERACTOREXITCODE -eq 1 ]]; then
|
11
|
+
echo "interactor return wrong answer"
|
12
|
+
exit 6
|
13
|
+
elif [[ $INTERACTOREXITCODE -eq 2 ]]; then
|
14
|
+
echo "interactor invalid input"
|
15
|
+
exit 6
|
16
|
+
elif [[ $INTERACTOREXITCODE -eq 3 ]]; then
|
17
|
+
echo "interactor failed with exit code 3"
|
18
|
+
exit 43
|
19
|
+
else
|
20
|
+
echo "interactor failed with exit code $INTERACTOREXITCODE"
|
21
|
+
exit 47
|
22
|
+
fi
|
23
|
+
fi
|
24
|
+
|
25
|
+
# Next lines of this script just compares team_output and sol_output,
|
26
|
+
# although it is possible to change them to more complex evaluations.
|
27
|
+
output=$(./checker.exe $3 $1 $2 2>&1 >/dev/null)
|
28
|
+
EC=$?
|
29
|
+
|
30
|
+
echo "checker exitcode = $EC"
|
31
|
+
echo "$output"
|
32
|
+
|
33
|
+
if [ $EC -eq 0 ]; then
|
34
|
+
echo "checker found no differences"
|
35
|
+
exit 4
|
36
|
+
elif [ $EC -eq 1 ]; then
|
37
|
+
echo "checker found differences"
|
38
|
+
exit 6
|
39
|
+
elif [ $EC -eq 2 ]; then
|
40
|
+
echo "checker found invalid output"
|
41
|
+
exit 6
|
42
|
+
elif [ $EC -eq 3 ]; then
|
43
|
+
echo "judge failed with $EC"
|
44
|
+
exit 43
|
45
|
+
else
|
46
|
+
echo "judge failed with $EC"
|
47
|
+
exit 47
|
48
|
+
fi
|