rbx.cp 0.5.53__py3-none-any.whl → 0.5.54__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 +13 -3
- rbx/grading/processing_context.py +43 -4
- rbx/grading/steps.py +58 -14
- {rbx_cp-0.5.53.dist-info → rbx_cp-0.5.54.dist-info}/METADATA +2 -1
- {rbx_cp-0.5.53.dist-info → rbx_cp-0.5.54.dist-info}/RECORD +8 -8
- {rbx_cp-0.5.53.dist-info → rbx_cp-0.5.54.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.53.dist-info → rbx_cp-0.5.54.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.53.dist-info → rbx_cp-0.5.54.dist-info}/entry_points.txt +0 -0
rbx/box/checkers.py
CHANGED
@@ -226,6 +226,10 @@ def _check_sanitizer_warnings(run_log: Optional[RunLog]) -> bool:
|
|
226
226
|
return run_log.warnings
|
227
227
|
|
228
228
|
|
229
|
+
def _is_testlib_eof(stderr: str) -> bool:
|
230
|
+
return 'wrong output format Unexpected end of file' in stderr
|
231
|
+
|
232
|
+
|
229
233
|
async def check(
|
230
234
|
checker_digest: str,
|
231
235
|
run_log: Optional[RunLog],
|
@@ -283,15 +287,21 @@ async def check_communication(
|
|
283
287
|
# interactor exited before it. Thus, check the interactor, as it might have
|
284
288
|
# returned a checker verdict.
|
285
289
|
#
|
286
|
-
# Also,
|
287
|
-
#
|
290
|
+
# Also, treat the case where the solution has a non-zero exit code similarly.
|
291
|
+
# This is aligned with what Polygon/Codeforces does: for some languages, we don't
|
292
|
+
# get a SIGPIPE. Instead, we get a non-zero exit code which we can't distinguish
|
293
|
+
# from a normal RTE. Thus, we decide that we should prioritize the interactor verdict
|
294
|
+
# over the solution's exit code in these cases.
|
288
295
|
if (
|
289
296
|
interactor_run_log is not None
|
290
297
|
and run_log is not None
|
291
298
|
and (
|
292
299
|
run_log.exitcode == -signal.SIGPIPE
|
293
300
|
or run_log.exitstatus == SandboxBase.EXIT_TERMINATED
|
294
|
-
or
|
301
|
+
or (
|
302
|
+
run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
|
303
|
+
and not _is_testlib_eof(interactor_stderr.read_text())
|
304
|
+
)
|
295
305
|
)
|
296
306
|
):
|
297
307
|
result = _check_interactor()
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
import contextlib
|
2
3
|
import os
|
3
4
|
import signal
|
@@ -5,22 +6,26 @@ import threading
|
|
5
6
|
from typing import Optional, Set
|
6
7
|
|
7
8
|
_processing_context_pids: Optional[Set[int]] = None
|
9
|
+
_terminate_all_on_error = False
|
8
10
|
_lock = threading.Lock()
|
9
11
|
|
10
12
|
# Creating a processing context is not thread-safe, but adding to it is.
|
11
13
|
|
12
14
|
|
13
15
|
@contextlib.contextmanager
|
14
|
-
def new_processing_context():
|
15
|
-
global _processing_context_pids
|
16
|
+
def new_processing_context(terminate_all_on_error: bool = False):
|
17
|
+
global _processing_context_pids, _terminate_all_on_error
|
16
18
|
with _lock:
|
17
19
|
old_processing_context_pids = _processing_context_pids
|
20
|
+
_old_terminate_all_on_error = _terminate_all_on_error
|
18
21
|
_processing_context_pids = set()
|
22
|
+
_terminate_all_on_error = terminate_all_on_error
|
19
23
|
try:
|
20
24
|
yield
|
21
25
|
finally:
|
22
26
|
with _lock:
|
23
27
|
_processing_context_pids = old_processing_context_pids
|
28
|
+
_terminate_all_on_error = _old_terminate_all_on_error
|
24
29
|
|
25
30
|
|
26
31
|
def get_processing_context() -> Set[int]:
|
@@ -36,7 +41,8 @@ def add_to_processing_context(pid: int):
|
|
36
41
|
_processing_context_pids.add(pid)
|
37
42
|
|
38
43
|
|
39
|
-
def terminate_all_processes_in_context():
|
44
|
+
def terminate_all_processes_in_context(clear: bool = True):
|
45
|
+
global _processing_context_pids
|
40
46
|
with _lock:
|
41
47
|
if _processing_context_pids is None:
|
42
48
|
return
|
@@ -45,4 +51,37 @@ def terminate_all_processes_in_context():
|
|
45
51
|
os.kill(pid, signal.SIGTERM)
|
46
52
|
except OSError:
|
47
53
|
pass
|
48
|
-
|
54
|
+
if clear:
|
55
|
+
_processing_context_pids.clear()
|
56
|
+
|
57
|
+
|
58
|
+
async def wait_all_processes_in_context(wait_for: int):
|
59
|
+
global _processing_context_pids, _terminate_all_on_error
|
60
|
+
wait_pids = set()
|
61
|
+
while len(get_processing_context()) < wait_for:
|
62
|
+
await asyncio.sleep(0.01)
|
63
|
+
|
64
|
+
with _lock:
|
65
|
+
if _processing_context_pids is None:
|
66
|
+
return
|
67
|
+
wait_pids.update(_processing_context_pids)
|
68
|
+
|
69
|
+
wait_lock = threading.Lock()
|
70
|
+
finished_pids = []
|
71
|
+
|
72
|
+
def process(pid: int, returncode: int):
|
73
|
+
with wait_lock:
|
74
|
+
finished_pids.append(pid)
|
75
|
+
if returncode != 0 and _terminate_all_on_error:
|
76
|
+
terminate_all_processes_in_context()
|
77
|
+
|
78
|
+
def wait_all_processes():
|
79
|
+
while len(finished_pids) < len(wait_pids):
|
80
|
+
try:
|
81
|
+
pid, status = os.wait()
|
82
|
+
except ChildProcessError:
|
83
|
+
return
|
84
|
+
if pid in wait_pids:
|
85
|
+
process(pid, os.waitstatus_to_exitcode(status))
|
86
|
+
|
87
|
+
await asyncio.to_thread(wait_all_processes)
|
rbx/grading/steps.py
CHANGED
@@ -193,6 +193,11 @@ class RunLogMetadata(BaseModel):
|
|
193
193
|
retryIndex: Optional[int] = None
|
194
194
|
|
195
195
|
|
196
|
+
class ProcessingContextLog(BaseModel):
|
197
|
+
pid: int = -1
|
198
|
+
exitindex: int = -1
|
199
|
+
|
200
|
+
|
196
201
|
class RunLog(BaseModel):
|
197
202
|
exitcode: int = 0
|
198
203
|
exitstatus: str = SandboxBase.EXIT_SANDBOX_ERROR
|
@@ -330,14 +335,29 @@ def _expand_part(part: str, sandbox: SandboxBase) -> List[str]:
|
|
330
335
|
return [part]
|
331
336
|
|
332
337
|
|
338
|
+
def _get_java_memory_limits(sandbox: SandboxBase) -> Tuple[int, int]:
|
339
|
+
max_memory = sandbox.params.address_space
|
340
|
+
if max_memory is None:
|
341
|
+
max_memory = 2048
|
342
|
+
return max_memory, min(512, int(max_memory * 0.9))
|
343
|
+
|
344
|
+
|
333
345
|
def _split_and_expand(command: str, sandbox: SandboxBase) -> List[str]:
|
334
346
|
res = []
|
335
|
-
|
347
|
+
max_mem, init_mem = _get_java_memory_limits(sandbox)
|
348
|
+
parts = shlex.split(command.format(memory=max_mem, initialMemory=init_mem))
|
336
349
|
for part in parts:
|
337
350
|
res.extend(_expand_part(part, sandbox))
|
338
351
|
return res
|
339
352
|
|
340
353
|
|
354
|
+
def get_exe_from_command(command: str) -> str:
|
355
|
+
cmds = shlex.split(command)
|
356
|
+
if not cmds:
|
357
|
+
return command
|
358
|
+
return cmds[0]
|
359
|
+
|
360
|
+
|
341
361
|
def _is_c_command(exe_command: str) -> bool:
|
342
362
|
return 'gcc' in exe_command or 'clang' in exe_command
|
343
363
|
|
@@ -351,15 +371,26 @@ def is_cxx_command(exe_command: str) -> bool:
|
|
351
371
|
|
352
372
|
|
353
373
|
def is_cxx_sanitizer_command(command: str) -> bool:
|
354
|
-
|
355
|
-
if not
|
374
|
+
exe = get_exe_from_command(command)
|
375
|
+
if not exe:
|
356
376
|
return False
|
357
|
-
exe = cmds[0]
|
358
377
|
if not is_cxx_command(exe):
|
359
378
|
return False
|
360
379
|
return 'fsanitize' in command
|
361
380
|
|
362
381
|
|
382
|
+
def is_java_command(exe_command: str) -> bool:
|
383
|
+
return 'javac' in exe_command or 'java' in exe_command
|
384
|
+
|
385
|
+
|
386
|
+
def is_kotlin_command(exe_command: str) -> bool:
|
387
|
+
return 'kotlinc' in exe_command or 'kotlin' in exe_command
|
388
|
+
|
389
|
+
|
390
|
+
def is_java_like_command(exe_command: str) -> bool:
|
391
|
+
return is_java_command(exe_command) or is_kotlin_command(exe_command)
|
392
|
+
|
393
|
+
|
363
394
|
@functools.cache
|
364
395
|
def _complain_about_clang() -> None:
|
365
396
|
console.print(
|
@@ -539,6 +570,10 @@ def compile(
|
|
539
570
|
stderr_file = pathlib.PosixPath(f'compile-{i}.stderr')
|
540
571
|
sandbox.params.set_stdall(stdout=stdout_file, stderr=stderr_file)
|
541
572
|
|
573
|
+
# Remove memory constraints for Java.
|
574
|
+
if is_java_like_command(get_exe_from_command(command)):
|
575
|
+
sandbox.params.address_space = None
|
576
|
+
|
542
577
|
if bits_artifact is not None and _is_cpp_command(cmd[0]):
|
543
578
|
# Include from sandbox directory to import bits/stdc++.h.
|
544
579
|
cmd.append('-I.')
|
@@ -604,6 +639,10 @@ async def run(
|
|
604
639
|
cmd = _split_and_expand(command, sandbox)
|
605
640
|
sandbox.set_params(params)
|
606
641
|
|
642
|
+
# Remove memory constraints for Java.
|
643
|
+
if is_java_like_command(get_exe_from_command(command)):
|
644
|
+
sandbox.params.address_space = None
|
645
|
+
|
607
646
|
if not await asyncio.to_thread(sandbox.execute_without_std, cmd):
|
608
647
|
console.print(
|
609
648
|
'[error]Sandbox crashed while processing command:[/error]',
|
@@ -614,7 +653,7 @@ async def run(
|
|
614
653
|
return None
|
615
654
|
|
616
655
|
if sandbox.get_exit_code() != 0 and kill_on_processing_context_exit:
|
617
|
-
processing_context.terminate_all_processes_in_context()
|
656
|
+
processing_context.terminate_all_processes_in_context(clear=False)
|
618
657
|
|
619
658
|
if not _process_output_artifacts(artifacts, sandbox):
|
620
659
|
return None
|
@@ -657,22 +696,27 @@ async def run_coordinated(
|
|
657
696
|
interactor: CoordinatedRunParams,
|
658
697
|
solution: CoordinatedRunParams,
|
659
698
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
660
|
-
with processing_context.new_processing_context():
|
699
|
+
with processing_context.new_processing_context(terminate_all_on_error=True):
|
700
|
+
# Schedule both runs to execute immediately.
|
661
701
|
runs = tuple(
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
702
|
+
asyncio.create_task(
|
703
|
+
run(
|
704
|
+
params.command,
|
705
|
+
params.params,
|
706
|
+
params.sandbox,
|
707
|
+
params.artifacts,
|
708
|
+
params.metadata,
|
709
|
+
kill_on_processing_context_exit=True,
|
710
|
+
)
|
669
711
|
)
|
670
712
|
for params in [interactor, solution]
|
671
713
|
)
|
672
|
-
|
714
|
+
await processing_context.wait_all_processes_in_context(wait_for=2)
|
715
|
+
logs = typing.cast(
|
673
716
|
Tuple[Optional[RunLog], Optional[RunLog]],
|
674
717
|
tuple(await asyncio.gather(*runs)),
|
675
718
|
)
|
719
|
+
return logs
|
676
720
|
|
677
721
|
|
678
722
|
def _normalize_checked_words(s: str) -> Tuple[str, ...]:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: rbx.cp
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.54
|
4
4
|
Summary:
|
5
5
|
Author: Roberto Sales
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -21,6 +21,7 @@ Requires-Dist: latexbuild (>=0.2.2,<0.3.0)
|
|
21
21
|
Requires-Dist: mechanize (>=0.4.10,<0.5.0)
|
22
22
|
Requires-Dist: more-itertools (>=10.5.0,<11.0.0)
|
23
23
|
Requires-Dist: nest-asyncio (>=1.6.0,<2.0.0)
|
24
|
+
Requires-Dist: psutil (>=7.0.0,<8.0.0)
|
24
25
|
Requires-Dist: pydantic (==2.8.2)
|
25
26
|
Requires-Dist: pydantic-xml[lxml] (>=2.11.0,<3.0.0)
|
26
27
|
Requires-Dist: pyte (>=0.8.2,<0.9.0)
|
@@ -4,7 +4,7 @@ rbx/autoenum.py,sha256=cusv8ClXRlDVvhZ8eDrtYcL_2peXlHugAey_ht8roXk,12025
|
|
4
4
|
rbx/box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
rbx/box/builder.py,sha256=MDm2qqmhedAbhn3rWP6cDwbBsGhV6sz_2sg1zLkPDw0,3613
|
6
6
|
rbx/box/cd.py,sha256=9a_SOnzoJBXxxffp4Wbf3UKXIwKuN3Hvj7K6SocALwE,1194
|
7
|
-
rbx/box/checkers.py,sha256=
|
7
|
+
rbx/box/checkers.py,sha256=5Pj6a8y5YCVY3KvV7B_WLo30uDmIhnEgxE6_KKZqzvY,11923
|
8
8
|
rbx/box/cli.py,sha256=GPoGDgmaV1lXL9puXhUbZUIEU0lOMvdH1kD4TGCXkCo,26738
|
9
9
|
rbx/box/code.py,sha256=hmA2EoGOr13AYzicHNsnU2SkFW-44l7kOdu8QRwTJlI,18848
|
10
10
|
rbx/box/compile.py,sha256=OJLthDQ921w9vyoE6Gk1Df54i5RwtRJ2YG-8XEfefcs,2489
|
@@ -98,8 +98,8 @@ rbx/grading/judge/sandboxes/timeit.py,sha256=jQonQhaVI93diYQsJYXwS77IO1t5iZVKJtm
|
|
98
98
|
rbx/grading/judge/storage.py,sha256=3vv0HvtenbUZBH33CB5ZzX66ppL22G6munBaAA9BgwQ,9418
|
99
99
|
rbx/grading/judge/test.py,sha256=ll0Iw7zyOpGdKPD_PGH7dvUkb4stQLu-ikbQnqJvuAc,944
|
100
100
|
rbx/grading/judge/testiso.py,sha256=v14DtkWiZFJ9AKMzrb0_vZKPWDt8jz8iIw1Z2O-Advk,1397
|
101
|
-
rbx/grading/processing_context.py,sha256=
|
102
|
-
rbx/grading/steps.py,sha256=
|
101
|
+
rbx/grading/processing_context.py,sha256=EOxsRTKB_JEgcKNodDWPIYaBramANU-6QnDkqdF8tEk,2556
|
102
|
+
rbx/grading/steps.py,sha256=VscFO-fnKtSspdB1Cnu18l8feaREbsh0MNkexJ6obOc,27163
|
103
103
|
rbx/grading/steps_with_caching.py,sha256=nez2YwgauGXKRjhk6tQxTDGQ-HEk7KfZOeAPhsxi5iw,3150
|
104
104
|
rbx/grading/steps_with_caching_run_test.py,sha256=mh4DRInrOGhnQFWD1SlcjDm_HvcSDFTDMSpAlG-Q5SI,15570
|
105
105
|
rbx/grading_utils.py,sha256=lL2KtSkOsMElqrRoApQTbFcqVOeHVWUDTMCa3IsLpC4,4484
|
@@ -193,8 +193,8 @@ rbx/testcase.py,sha256=yKOq3CAJZ1YTmInvnoIs0u1iJnRj_X85XiWbLI-p9d8,1951
|
|
193
193
|
rbx/testcase_rendering.py,sha256=nfmv6dSEqd4aR3TsaODwkKGK6AXty_DDKtWf_ejiQpI,2084
|
194
194
|
rbx/testing_utils.py,sha256=x_PqD8Zd2PkN91NxVHUnSTs044-1WK5KKtttKQBXpFs,2083
|
195
195
|
rbx/utils.py,sha256=SfR844_i0ebRDMkmS_w1YdZiWPc6h2RGADygewlWRbA,4845
|
196
|
-
rbx_cp-0.5.
|
197
|
-
rbx_cp-0.5.
|
198
|
-
rbx_cp-0.5.
|
199
|
-
rbx_cp-0.5.
|
200
|
-
rbx_cp-0.5.
|
196
|
+
rbx_cp-0.5.54.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
197
|
+
rbx_cp-0.5.54.dist-info/METADATA,sha256=Ki-VbHPb5TES3dtzJ_muEqjIuzJRACCQ5MIhdIGG0G4,3387
|
198
|
+
rbx_cp-0.5.54.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
199
|
+
rbx_cp-0.5.54.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
|
200
|
+
rbx_cp-0.5.54.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|