rbx.cp 0.5.46__py3-none-any.whl → 0.5.47__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/packaging/boca/packager.py +44 -7
- rbx/box/packaging/main.py +7 -0
- rbx/box/packaging/moj/packager.py +88 -8
- rbx/box/packaging/packager.py +7 -2
- rbx/box/packaging/polygon/packager.py +5 -4
- rbx/box/solutions.py +2 -2
- 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 +24 -13
- 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.47.dist-info}/METADATA +1 -1
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/RECORD +57 -30
- rbx/resources/packagers/boca/compare +0 -53
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.46.dist-info → rbx_cp-0.5.47.dist-info}/entry_points.txt +0 -0
rbx/box/checkers.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import pathlib
|
2
|
+
import signal
|
2
3
|
from typing import Optional
|
3
4
|
|
4
5
|
import typer
|
@@ -108,23 +109,31 @@ def _convert_tle(result: CheckerResult, run_log: Optional[RunLog]) -> CheckerRes
|
|
108
109
|
return result
|
109
110
|
|
110
111
|
|
112
|
+
def _is_checker_exitcode(exitcode: int) -> bool:
|
113
|
+
return exitcode in [0, 1, 2, 3]
|
114
|
+
|
115
|
+
|
111
116
|
def process_checker_run_log(
|
112
117
|
checker_run_log: Optional[RunLog], message: str
|
113
|
-
) ->
|
118
|
+
) -> CheckerResult:
|
114
119
|
if (
|
115
120
|
checker_run_log is not None
|
116
|
-
and checker_run_log.
|
117
|
-
and (
|
118
|
-
checker_run_log.exitstatus != SandboxBase.EXIT_NONZERO_RETURN
|
119
|
-
or checker_run_log.exitcode not in [0, 1, 2, 3]
|
120
|
-
)
|
121
|
+
and checker_run_log.exitstatus == SandboxBase.EXIT_SANDBOX_ERROR
|
121
122
|
):
|
122
|
-
|
123
|
+
# When the sandbox fails, it means the checker failed to run.
|
124
|
+
# We don't know what happened.
|
125
|
+
return CheckerResult(
|
126
|
+
outcome=Outcome.INTERNAL_ERROR,
|
127
|
+
message='sandbox failed to run checker',
|
128
|
+
)
|
123
129
|
|
124
130
|
if checker_run_log is None:
|
125
131
|
return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
|
126
|
-
if checker_run_log.exitcode
|
127
|
-
return
|
132
|
+
if not _is_checker_exitcode(checker_run_log.exitcode):
|
133
|
+
return CheckerResult(
|
134
|
+
outcome=Outcome.JUDGE_FAILED,
|
135
|
+
message=f'checker failed with unknown exit code {checker_run_log.exitcode}: {message}',
|
136
|
+
)
|
128
137
|
|
129
138
|
result = CheckerResult(outcome=Outcome.ACCEPTED, message=message)
|
130
139
|
|
@@ -185,7 +194,7 @@ async def _check(
|
|
185
194
|
message = package.get_digest_as_string(error.value or '') or ''
|
186
195
|
|
187
196
|
processed_checker_result = process_checker_run_log(checker_run_log, message)
|
188
|
-
if processed_checker_result
|
197
|
+
if processed_checker_result.outcome == Outcome.INTERNAL_ERROR:
|
189
198
|
console.console.print(
|
190
199
|
f'[error]Checker [item]{package.get_checker().path}[/item] failed unexpectedly.[/error]'
|
191
200
|
)
|
@@ -240,33 +249,77 @@ async def check_communication(
|
|
240
249
|
program_output: pathlib.Path,
|
241
250
|
skip_run_log: bool = False,
|
242
251
|
) -> CheckerResult:
|
243
|
-
|
252
|
+
def _extra_check_and_sanitize(result: CheckerResult) -> CheckerResult:
|
253
|
+
result.sanitizer_warnings = _check_sanitizer_warnings(run_log)
|
254
|
+
return result
|
255
|
+
|
256
|
+
def _check_interactor(reinterpret_rte: bool = True) -> Optional[CheckerResult]:
|
257
|
+
result = process_checker_run_log(
|
258
|
+
interactor_run_log, interactor_stderr.read_text()
|
259
|
+
)
|
260
|
+
if result.outcome in [Outcome.JUDGE_FAILED, Outcome.WRONG_ANSWER]:
|
261
|
+
# Only return testlib errors (exit code 2 and 3), skip other types of RTEs and verdicts.
|
262
|
+
if (
|
263
|
+
interactor_run_log is not None
|
264
|
+
and _is_checker_exitcode(interactor_run_log.exitcode)
|
265
|
+
and interactor_run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
|
266
|
+
):
|
267
|
+
return _extra_check_and_sanitize(result)
|
268
|
+
else:
|
269
|
+
# Check for other verdicts, but potentially reinterpret RTEs as JUDGE_FAILED.
|
270
|
+
result = check_with_no_output(interactor_run_log)
|
271
|
+
if result.outcome == Outcome.RUNTIME_ERROR and reinterpret_rte:
|
272
|
+
result.outcome = Outcome.JUDGE_FAILED
|
273
|
+
if result.outcome != Outcome.ACCEPTED:
|
274
|
+
return _extra_check_and_sanitize(result)
|
275
|
+
else:
|
276
|
+
# Return any other checker/interactor errors, such as INTERNAL_ERRORs.
|
277
|
+
return _extra_check_and_sanitize(result)
|
278
|
+
|
279
|
+
# No relevant error was found.
|
280
|
+
return None
|
244
281
|
|
282
|
+
# 1. If the solution received SIGPIPE or was terminated, it means the
|
283
|
+
# interactor exited before it. Thus, check the interactor, as it might have
|
284
|
+
# returned a checker verdict.
|
285
|
+
#
|
286
|
+
# Also, do the same if the solution returned a non-zero exit code. Checker verdict
|
287
|
+
# should usually supersede a solution's RTE verdict.
|
288
|
+
if (
|
289
|
+
interactor_run_log is not None
|
290
|
+
and run_log is not None
|
291
|
+
and (
|
292
|
+
run_log.exitcode == -signal.SIGPIPE
|
293
|
+
or run_log.exitstatus == SandboxBase.EXIT_TERMINATED
|
294
|
+
or run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
|
295
|
+
)
|
296
|
+
):
|
297
|
+
result = _check_interactor()
|
298
|
+
if result is not None and result.outcome != Outcome.ACCEPTED:
|
299
|
+
return _extra_check_and_sanitize(result)
|
300
|
+
|
301
|
+
# 2. Check if the solution failed without looking at its output (TLE, MLE, RTE, etc).
|
245
302
|
result = check_with_no_output(run_log)
|
246
|
-
result.sanitizer_warnings = sanitizer_warnings
|
247
303
|
if result.outcome != Outcome.ACCEPTED:
|
248
|
-
return result
|
304
|
+
return _extra_check_and_sanitize(result)
|
249
305
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
if result is None:
|
256
|
-
result = check_with_no_output(interactor_run_log)
|
257
|
-
result.sanitizer_warnings = sanitizer_warnings
|
258
|
-
if result.outcome != Outcome.ACCEPTED:
|
259
|
-
result.outcome = Outcome.JUDGE_FAILED
|
260
|
-
return result
|
306
|
+
# 3. Now check interactor return code regardless of what happened to the
|
307
|
+
# solution.
|
308
|
+
result = _check_interactor()
|
309
|
+
if result is not None and result.outcome != Outcome.ACCEPTED:
|
310
|
+
return _extra_check_and_sanitize(result)
|
261
311
|
|
262
|
-
result
|
312
|
+
# Just a defensive pattern to ensure result is not None, should never happen.
|
313
|
+
result = check_with_no_output(interactor_run_log)
|
263
314
|
if result.outcome != Outcome.ACCEPTED:
|
264
|
-
|
315
|
+
if result.outcome == Outcome.RUNTIME_ERROR:
|
316
|
+
result.outcome = Outcome.JUDGE_FAILED
|
317
|
+
return _extra_check_and_sanitize(result)
|
265
318
|
|
319
|
+
# 4. Now actually check the output with a checker.
|
266
320
|
if checker_digest is not None:
|
267
321
|
result = await check(
|
268
322
|
checker_digest, run_log, testcase, program_output, skip_run_log
|
269
323
|
)
|
270
|
-
result.sanitizer_warnings = sanitizer_warnings
|
271
324
|
|
272
|
-
return result
|
325
|
+
return _extra_check_and_sanitize(result)
|
rbx/box/cli.py
CHANGED
@@ -222,7 +222,7 @@ async def run(
|
|
222
222
|
tracked_solutions = {solution}
|
223
223
|
|
224
224
|
if choice:
|
225
|
-
tracked_solutions = set(pick_solutions(tracked_solutions))
|
225
|
+
tracked_solutions = set(await pick_solutions(tracked_solutions))
|
226
226
|
if not tracked_solutions:
|
227
227
|
console.console.print('[error]No solutions selected. Exiting.[/error]')
|
228
228
|
raise typer.Exit(1)
|
@@ -453,7 +453,7 @@ async def irun(
|
|
453
453
|
tracked_solutions = {solution}
|
454
454
|
|
455
455
|
if choice:
|
456
|
-
tracked_solutions = set(pick_solutions(tracked_solutions))
|
456
|
+
tracked_solutions = set(await pick_solutions(tracked_solutions))
|
457
457
|
if not tracked_solutions:
|
458
458
|
console.console.print('[error]No solutions selected. Exiting.[/error]')
|
459
459
|
raise typer.Exit(1)
|
@@ -515,7 +515,8 @@ def create(
|
|
515
515
|
help='Run a stress test.',
|
516
516
|
)
|
517
517
|
@package.within_problem
|
518
|
-
|
518
|
+
@syncer.sync
|
519
|
+
async def stress(
|
519
520
|
name: Annotated[
|
520
521
|
str,
|
521
522
|
typer.Argument(
|
@@ -574,7 +575,7 @@ def stress(
|
|
574
575
|
from rbx.box import stresses
|
575
576
|
|
576
577
|
with utils.StatusProgress('Running stress...') as s:
|
577
|
-
report = stresses.run_stress(
|
578
|
+
report = await stresses.run_stress(
|
578
579
|
name,
|
579
580
|
timeout,
|
580
581
|
args=generator_args,
|
@@ -608,15 +609,15 @@ def stress(
|
|
608
609
|
|
609
610
|
import questionary
|
610
611
|
|
611
|
-
testgroup = questionary.select(
|
612
|
+
testgroup = await questionary.select(
|
612
613
|
'Choose the testgroup to add the tests to.\nOnly test groups that have a .txt generatorScript are shown below: ',
|
613
614
|
choices=list(groups_by_name) + ['(create new script)', '(skip)'],
|
614
|
-
).
|
615
|
+
).ask_async()
|
615
616
|
|
616
617
|
if testgroup == '(create new script)':
|
617
|
-
new_script_name = questionary.text(
|
618
|
+
new_script_name = await questionary.text(
|
618
619
|
'Enter the name of the new .txt generatorScript file: '
|
619
|
-
).
|
620
|
+
).ask_async()
|
620
621
|
new_script_path = pathlib.Path(new_script_name).with_suffix('.txt')
|
621
622
|
new_script_path.parent.mkdir(parents=True, exist_ok=True)
|
622
623
|
new_script_path.touch()
|
@@ -673,7 +674,8 @@ def stress(
|
|
673
674
|
help='Compile an asset given its path.',
|
674
675
|
)
|
675
676
|
@package.within_problem
|
676
|
-
|
677
|
+
@syncer.sync
|
678
|
+
async def compile_command(
|
677
679
|
path: Annotated[
|
678
680
|
Optional[str],
|
679
681
|
typer.Argument(help='Path to the asset to compile.'),
|
@@ -694,7 +696,7 @@ def compile_command(
|
|
694
696
|
if path is None:
|
695
697
|
import questionary
|
696
698
|
|
697
|
-
path = questionary.path("What's the path to your asset?").
|
699
|
+
path = await questionary.path("What's the path to your asset?").ask_async()
|
698
700
|
if path is None:
|
699
701
|
console.console.print('[error]No path specified.[/error]')
|
700
702
|
raise typer.Exit(1)
|
@@ -10,6 +10,7 @@ from rbx.box import package
|
|
10
10
|
from rbx.box.environment import get_extension_or_default
|
11
11
|
from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
|
12
12
|
from rbx.box.packaging.packager import BasePackager, BuiltStatement
|
13
|
+
from rbx.box.schema import TaskType
|
13
14
|
from rbx.box.statements.schema import Statement
|
14
15
|
from rbx.config import get_default_app_path, get_testlib
|
15
16
|
|
@@ -24,6 +25,10 @@ def test_time(time):
|
|
24
25
|
|
25
26
|
|
26
27
|
class BocaPackager(BasePackager):
|
28
|
+
@classmethod
|
29
|
+
def task_types(cls) -> List[TaskType]:
|
30
|
+
return [TaskType.BATCH, TaskType.COMMUNICATION]
|
31
|
+
|
27
32
|
def _get_main_statement(self) -> Statement:
|
28
33
|
pkg = package.find_problem_package_or_die()
|
29
34
|
|
@@ -119,7 +124,7 @@ class BocaPackager(BasePackager):
|
|
119
124
|
)
|
120
125
|
|
121
126
|
def _get_compare(self) -> str:
|
122
|
-
compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare'
|
127
|
+
compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare.sh'
|
123
128
|
if not compare_path.exists():
|
124
129
|
console.console.print(
|
125
130
|
'[error]BOCA template compare script not found.[/error]'
|
@@ -145,7 +150,26 @@ class BocaPackager(BasePackager):
|
|
145
150
|
.replace('{{checker_content}}', checker)
|
146
151
|
)
|
147
152
|
|
153
|
+
def _get_interactor(self) -> str:
|
154
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
155
|
+
|
156
|
+
interactor_path = (
|
157
|
+
get_default_app_path() / 'packagers' / 'boca' / 'interactor_compile.sh'
|
158
|
+
)
|
159
|
+
if not interactor_path.exists():
|
160
|
+
console.console.print(
|
161
|
+
'[error]BOCA template interactor compile script not found.[/error]'
|
162
|
+
)
|
163
|
+
raise typer.Exit(1)
|
164
|
+
|
165
|
+
interactor_text = interactor_path.read_text()
|
166
|
+
interactor = package.get_interactor().path.read_text()
|
167
|
+
return interactor_text.replace(
|
168
|
+
'{{rbxFlags}}', extension.flags_with_defaults()['cc']
|
169
|
+
).replace('{{interactor_content}}', interactor)
|
170
|
+
|
148
171
|
def _get_compile(self, language: BocaLanguage) -> str:
|
172
|
+
pkg = package.find_problem_package_or_die()
|
149
173
|
extension = get_extension_or_default('boca', BocaExtension)
|
150
174
|
|
151
175
|
compile_path = (
|
@@ -160,6 +184,10 @@ class BocaPackager(BasePackager):
|
|
160
184
|
compile_text = compile_path.read_text()
|
161
185
|
|
162
186
|
assert 'umask 0022' in compile_text
|
187
|
+
if pkg.type == TaskType.COMMUNICATION:
|
188
|
+
compile_text = compile_text.replace(
|
189
|
+
'umask 0022', 'umask 0022\n\n' + self._get_interactor()
|
190
|
+
)
|
163
191
|
compile_text = compile_text.replace(
|
164
192
|
'umask 0022', 'umask 0022\n\n' + self._get_checker()
|
165
193
|
)
|
@@ -197,7 +225,8 @@ class BocaPackager(BasePackager):
|
|
197
225
|
)
|
198
226
|
)
|
199
227
|
|
200
|
-
|
228
|
+
@classmethod
|
229
|
+
def name(cls) -> str:
|
201
230
|
return 'boca'
|
202
231
|
|
203
232
|
def package(
|
@@ -207,7 +236,7 @@ class BocaPackager(BasePackager):
|
|
207
236
|
built_statements: List[BuiltStatement],
|
208
237
|
) -> pathlib.Path:
|
209
238
|
extension = get_extension_or_default('boca', BocaExtension)
|
210
|
-
|
239
|
+
pkg = package.find_problem_package_or_die()
|
211
240
|
# Prepare limits
|
212
241
|
limits_path = into_path / 'limits'
|
213
242
|
limits_path.mkdir(parents=True, exist_ok=True)
|
@@ -227,9 +256,17 @@ class BocaPackager(BasePackager):
|
|
227
256
|
run_orig_path = (
|
228
257
|
get_default_app_path() / 'packagers' / 'boca' / 'run' / language
|
229
258
|
)
|
259
|
+
if pkg.type == TaskType.COMMUNICATION:
|
260
|
+
run_orig_path = (
|
261
|
+
get_default_app_path()
|
262
|
+
/ 'packagers'
|
263
|
+
/ 'boca'
|
264
|
+
/ 'interactive'
|
265
|
+
/ language
|
266
|
+
)
|
230
267
|
if not run_orig_path.is_file():
|
231
268
|
console.console.print(
|
232
|
-
f'[error]Run script for language [item]{language}[/item] not found.[/error]'
|
269
|
+
f'[error]Run script for language [item]{language}[/item] not found for task of type [item]{pkg.type}[/item].[/error]'
|
233
270
|
)
|
234
271
|
raise typer.Exit(1)
|
235
272
|
shutil.copyfile(run_orig_path, run_path / language)
|
@@ -268,11 +305,11 @@ class BocaPackager(BasePackager):
|
|
268
305
|
|
269
306
|
testcases = self.get_flattened_built_testcases()
|
270
307
|
for i, testcase in enumerate(testcases):
|
271
|
-
shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
|
308
|
+
shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
|
272
309
|
if testcase.outputPath is not None:
|
273
|
-
shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
|
310
|
+
shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
|
274
311
|
else:
|
275
|
-
(outputs_path / f'{i+1:03d}').touch()
|
312
|
+
(outputs_path / f'{i + 1:03d}').touch()
|
276
313
|
|
277
314
|
# Zip all.
|
278
315
|
shutil.make_archive(
|
rbx/box/packaging/main.py
CHANGED
@@ -28,6 +28,13 @@ async def run_packager(
|
|
28
28
|
raise typer.Exit(1)
|
29
29
|
|
30
30
|
pkg = package.find_problem_package_or_die()
|
31
|
+
|
32
|
+
if pkg.type not in packager_cls.task_types():
|
33
|
+
console.console.print(
|
34
|
+
f'[error]Packager [item]{packager_cls.name()}[/item] does not support task type [item]{pkg.type}[/item].[/error]'
|
35
|
+
)
|
36
|
+
raise typer.Exit(1)
|
37
|
+
|
31
38
|
packager = packager_cls(**kwargs)
|
32
39
|
|
33
40
|
statement_types = packager.statement_types()
|
@@ -7,10 +7,10 @@ import typer
|
|
7
7
|
from rbx import console
|
8
8
|
from rbx.box import package
|
9
9
|
from rbx.box.environment import get_extension_or_default
|
10
|
-
from rbx.box.packaging.boca.extension import BocaExtension
|
10
|
+
from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
|
11
11
|
from rbx.box.packaging.boca.packager import BocaPackager
|
12
12
|
from rbx.box.packaging.packager import BuiltStatement
|
13
|
-
from rbx.box.schema import ExpectedOutcome
|
13
|
+
from rbx.box.schema import ExpectedOutcome, TaskType
|
14
14
|
from rbx.config import get_default_app_path, get_testlib
|
15
15
|
from rbx.grading.judge.digester import digest_cooperatively
|
16
16
|
|
@@ -20,6 +20,10 @@ class MojPackager(BocaPackager):
|
|
20
20
|
super().__init__()
|
21
21
|
self.for_boca = for_boca
|
22
22
|
|
23
|
+
@classmethod
|
24
|
+
def task_types(cls) -> List[TaskType]:
|
25
|
+
return [TaskType.COMMUNICATION, TaskType.BATCH]
|
26
|
+
|
23
27
|
def _get_problem_info(self) -> str:
|
24
28
|
statement = self._get_main_statement()
|
25
29
|
return (
|
@@ -40,8 +44,12 @@ class MojPackager(BocaPackager):
|
|
40
44
|
def _get_limits(self) -> str:
|
41
45
|
pkg = package.find_problem_package_or_die()
|
42
46
|
ml = pkg.memoryLimit
|
43
|
-
ol = pkg.outputLimit
|
44
|
-
|
47
|
+
# ol = pkg.outputLimit
|
48
|
+
limits = [
|
49
|
+
f'ULIMITS[-v]={ml * 1024}',
|
50
|
+
# f'ULIMITS[-f]={ol}',
|
51
|
+
]
|
52
|
+
return '\n'.join(limits) + '\n'
|
45
53
|
|
46
54
|
def _get_compare(self) -> str:
|
47
55
|
extension = get_extension_or_default('boca', BocaExtension)
|
@@ -65,6 +73,33 @@ class MojPackager(BocaPackager):
|
|
65
73
|
def _get_checker(self) -> str:
|
66
74
|
return package.get_checker().path.read_text()
|
67
75
|
|
76
|
+
def _get_interactor(self) -> str:
|
77
|
+
return package.get_interactor().path.read_text()
|
78
|
+
|
79
|
+
def _expand_language_vars(self, language: BocaLanguage, dir: pathlib.Path):
|
80
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
81
|
+
|
82
|
+
for path in dir.glob('**/*'):
|
83
|
+
if not path.is_file():
|
84
|
+
continue
|
85
|
+
|
86
|
+
replaced = path.read_text()
|
87
|
+
replaced = replaced.replace(
|
88
|
+
'{{rbxMaxMemory}}', f'{self._get_pkg_memorylimit(language)}'
|
89
|
+
).replace(
|
90
|
+
'{{rbxInitialMemory}}',
|
91
|
+
f'{min(512, int(self._get_pkg_memorylimit(language) * 0.9))}',
|
92
|
+
)
|
93
|
+
|
94
|
+
flags = extension.flags_with_defaults()
|
95
|
+
if language in flags:
|
96
|
+
replaced = replaced.replace('{{rbxFlags}}', flags[language])
|
97
|
+
|
98
|
+
path.write_text(replaced)
|
99
|
+
|
100
|
+
if path.suffix == '.sh':
|
101
|
+
path.chmod(0o755)
|
102
|
+
|
68
103
|
def _copy_solutions_moj(self, into_path: pathlib.Path):
|
69
104
|
into_path = into_path / 'sols'
|
70
105
|
has_good = False
|
@@ -83,7 +118,8 @@ class MojPackager(BocaPackager):
|
|
83
118
|
console.console.print('[error]No good solution found.[/error]')
|
84
119
|
raise typer.Exit(1)
|
85
120
|
|
86
|
-
|
121
|
+
@classmethod
|
122
|
+
def name(cls) -> str:
|
87
123
|
return 'moj'
|
88
124
|
|
89
125
|
def package(
|
@@ -92,6 +128,8 @@ class MojPackager(BocaPackager):
|
|
92
128
|
into_path: pathlib.Path,
|
93
129
|
built_statements: List[BuiltStatement],
|
94
130
|
) -> pathlib.Path:
|
131
|
+
pkg = package.find_problem_package_or_die()
|
132
|
+
|
95
133
|
# Prepare dummy files
|
96
134
|
author_path = into_path / 'author'
|
97
135
|
author_path.parent.mkdir(parents=True, exist_ok=True)
|
@@ -128,6 +166,48 @@ class MojPackager(BocaPackager):
|
|
128
166
|
checker_path.parent.mkdir(parents=True, exist_ok=True)
|
129
167
|
checker_path.write_text(self._get_checker())
|
130
168
|
|
169
|
+
# Prepare interactor
|
170
|
+
if pkg.type == TaskType.COMMUNICATION:
|
171
|
+
interactor_path = into_path / 'scripts' / 'interactor.cpp'
|
172
|
+
interactor_path.parent.mkdir(parents=True, exist_ok=True)
|
173
|
+
interactor_path.write_text(self._get_interactor())
|
174
|
+
|
175
|
+
interactor_prep_path = into_path / 'scripts' / 'interactor_prep.sh'
|
176
|
+
interactor_prep_path.parent.mkdir(parents=True, exist_ok=True)
|
177
|
+
shutil.copy(
|
178
|
+
get_default_app_path()
|
179
|
+
/ 'packagers'
|
180
|
+
/ 'moj'
|
181
|
+
/ 'scripts'
|
182
|
+
/ 'interactor_prep.sh',
|
183
|
+
interactor_prep_path,
|
184
|
+
)
|
185
|
+
interactor_prep_path.chmod(0o755)
|
186
|
+
|
187
|
+
interactor_run_path = into_path / 'scripts' / 'interactor_run.sh'
|
188
|
+
interactor_run_path.parent.mkdir(parents=True, exist_ok=True)
|
189
|
+
shutil.copy(
|
190
|
+
get_default_app_path()
|
191
|
+
/ 'packagers'
|
192
|
+
/ 'moj'
|
193
|
+
/ 'scripts'
|
194
|
+
/ 'interactor_run.sh',
|
195
|
+
interactor_run_path,
|
196
|
+
)
|
197
|
+
interactor_run_path.chmod(0o755)
|
198
|
+
|
199
|
+
# Prepare language scripts
|
200
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
201
|
+
for language in extension.languages:
|
202
|
+
language_path = into_path / 'scripts' / language
|
203
|
+
language_path.parent.mkdir(parents=True, exist_ok=True)
|
204
|
+
src_path = (
|
205
|
+
get_default_app_path() / 'packagers' / 'moj' / 'scripts' / language
|
206
|
+
)
|
207
|
+
if src_path.exists():
|
208
|
+
shutil.copytree(src_path, language_path)
|
209
|
+
self._expand_language_vars(language, language_path)
|
210
|
+
|
131
211
|
# Problem statement
|
132
212
|
enunciado_path = into_path / 'docs' / 'enunciado.pdf'
|
133
213
|
enunciado_path.parent.mkdir(parents=True, exist_ok=True)
|
@@ -150,11 +230,11 @@ class MojPackager(BocaPackager):
|
|
150
230
|
|
151
231
|
testcases = self.get_flattened_built_testcases()
|
152
232
|
for i, testcase in enumerate(testcases):
|
153
|
-
shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
|
233
|
+
shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
|
154
234
|
if testcase.outputPath is not None:
|
155
|
-
shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
|
235
|
+
shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
|
156
236
|
else:
|
157
|
-
(outputs_path / f'{i+1:03d}').touch()
|
237
|
+
(outputs_path / f'{i + 1:03d}').touch()
|
158
238
|
|
159
239
|
# Zip all.
|
160
240
|
shutil.make_archive(
|
rbx/box/packaging/packager.py
CHANGED
@@ -7,7 +7,7 @@ from rbx.box import package
|
|
7
7
|
from rbx.box.contest import contest_package
|
8
8
|
from rbx.box.contest.schema import ContestProblem, ContestStatement
|
9
9
|
from rbx.box.generators import get_all_built_testcases
|
10
|
-
from rbx.box.schema import Package, Testcase, TestcaseGroup
|
10
|
+
from rbx.box.schema import Package, TaskType, Testcase, TestcaseGroup
|
11
11
|
from rbx.box.statements.schema import Statement, StatementType
|
12
12
|
|
13
13
|
|
@@ -33,10 +33,15 @@ class BuiltProblemPackage:
|
|
33
33
|
|
34
34
|
|
35
35
|
class BasePackager(ABC):
|
36
|
+
@classmethod
|
36
37
|
@abstractmethod
|
37
|
-
def name(
|
38
|
+
def name(cls) -> str:
|
38
39
|
pass
|
39
40
|
|
41
|
+
@classmethod
|
42
|
+
def task_types(cls) -> List[TaskType]:
|
43
|
+
return [TaskType.BATCH]
|
44
|
+
|
40
45
|
def languages(self):
|
41
46
|
pkg = package.find_problem_package_or_die()
|
42
47
|
|
@@ -188,15 +188,15 @@ class PolygonPackager(BasePackager):
|
|
188
188
|
for i, testcase in enumerate(testcases):
|
189
189
|
shutil.copyfile(
|
190
190
|
testcase.inputPath,
|
191
|
-
into_path / f'tests/{i+1:03d}',
|
191
|
+
into_path / f'tests/{i + 1:03d}',
|
192
192
|
)
|
193
193
|
if testcase.outputPath is not None:
|
194
194
|
shutil.copyfile(
|
195
195
|
testcase.outputPath,
|
196
|
-
into_path / f'tests/{i+1:03d}.a',
|
196
|
+
into_path / f'tests/{i + 1:03d}.a',
|
197
197
|
)
|
198
198
|
else:
|
199
|
-
(into_path / f'tests/{i+1:03d}.a').touch()
|
199
|
+
(into_path / f'tests/{i + 1:03d}.a').touch()
|
200
200
|
|
201
201
|
# Write problem.xml
|
202
202
|
(into_path / 'problem.xml').write_text(descriptor)
|
@@ -208,7 +208,8 @@ class PolygonPackager(BasePackager):
|
|
208
208
|
|
209
209
|
|
210
210
|
class PolygonContestPackager(BaseContestPackager):
|
211
|
-
|
211
|
+
@classmethod
|
212
|
+
def name(cls) -> str:
|
212
213
|
return 'polygon'
|
213
214
|
|
214
215
|
def _get_names(self) -> List[polygon_schema.Name]:
|
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
|
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
|
)
|