rbx.cp 0.5.16__py3-none-any.whl → 0.5.18__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/builder.py +2 -2
- rbx/box/cd.py +12 -2
- rbx/box/checkers.py +38 -4
- rbx/box/code.py +128 -18
- rbx/box/compile.py +40 -8
- rbx/box/contest/build_contest_statements.py +7 -6
- rbx/box/contest/contest_package.py +15 -2
- rbx/box/contest/contest_utils.py +0 -9
- rbx/box/contest/main.py +35 -24
- rbx/box/contest/statements.py +4 -5
- rbx/box/deferred.py +26 -0
- rbx/box/extensions.py +0 -8
- rbx/box/generators.py +6 -4
- rbx/box/main.py +195 -19
- rbx/box/package.py +24 -2
- rbx/box/packaging/contest_main.py +5 -5
- rbx/box/sanitizers/warning_stack.py +90 -0
- rbx/box/schema.py +15 -1
- rbx/box/setter_config.py +132 -0
- rbx/box/solutions.py +237 -149
- rbx/box/solutions_test.py +2 -1
- rbx/box/stresses.py +5 -3
- rbx/box/validators.py +5 -5
- rbx/config.py +3 -0
- rbx/grading/caching.py +4 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +2 -0
- rbx/grading/judge/sandboxes/timeit.py +3 -3
- rbx/grading/steps.py +143 -15
- rbx/grading/steps_with_caching.py +2 -0
- rbx/resources/default_setter_config.mac.yml +31 -0
- rbx/resources/default_setter_config.yml +29 -0
- rbx/utils.py +8 -1
- {rbx_cp-0.5.16.dist-info → rbx_cp-0.5.18.dist-info}/METADATA +2 -1
- {rbx_cp-0.5.16.dist-info → rbx_cp-0.5.18.dist-info}/RECORD +37 -32
- {rbx_cp-0.5.16.dist-info → rbx_cp-0.5.18.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.16.dist-info → rbx_cp-0.5.18.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.16.dist-info → rbx_cp-0.5.18.dist-info}/entry_points.txt +0 -0
rbx/box/solutions_test.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
import pathlib
|
2
3
|
|
3
4
|
import pytest
|
@@ -20,7 +21,7 @@ def test_solutions(pkg_from_testdata: pathlib.Path):
|
|
20
21
|
generate_outputs_for_testcases()
|
21
22
|
|
22
23
|
result = run_solutions(verification=VerificationLevel.FULL)
|
23
|
-
res = convert_list_of_solution_evaluations_to_dict(result.items)
|
24
|
+
res = asyncio.run(convert_list_of_solution_evaluations_to_dict(result.items))
|
24
25
|
|
25
26
|
# First solution should pass all tests.
|
26
27
|
assert all(chk.result.outcome == Outcome.ACCEPTED for chk in res[0]['gen1'])
|
rbx/box/stresses.py
CHANGED
@@ -8,7 +8,7 @@ from pydantic import BaseModel
|
|
8
8
|
|
9
9
|
from rbx import console
|
10
10
|
from rbx.box import checkers, package, validators
|
11
|
-
from rbx.box.code import compile_item, run_item
|
11
|
+
from rbx.box.code import SanitizationLevel, compile_item, run_item
|
12
12
|
from rbx.box.generators import generate_standalone
|
13
13
|
from rbx.box.schema import CodeItem, GeneratorCall, Stress, Testcase
|
14
14
|
from rbx.box.solutions import compile_solutions, get_outcome_style_verdict
|
@@ -49,6 +49,7 @@ def run_stress(
|
|
49
49
|
findingsLimit: int = 1,
|
50
50
|
verbose: bool = False,
|
51
51
|
progress: Optional[StatusProgress] = None,
|
52
|
+
sanitized: bool = False,
|
52
53
|
) -> StressReport:
|
53
54
|
if finder:
|
54
55
|
stress = Stress(
|
@@ -63,7 +64,7 @@ def run_stress(
|
|
63
64
|
generator = package.get_generator(call.name)
|
64
65
|
|
65
66
|
try:
|
66
|
-
generator_digest = compile_item(generator)
|
67
|
+
generator_digest = compile_item(generator, sanitized=SanitizationLevel.PREFER)
|
67
68
|
except:
|
68
69
|
console.console.print(
|
69
70
|
f'[error]Failed compiling generator [item]{generator.name}[/item].[/error]'
|
@@ -79,7 +80,8 @@ def run_stress(
|
|
79
80
|
|
80
81
|
solution_indices = {str(solution.path): i for i, solution in enumerate(solutions)}
|
81
82
|
solutions_digest = compile_solutions(
|
82
|
-
tracked_solutions=set(str(solution.path) for solution in solutions)
|
83
|
+
tracked_solutions=set(str(solution.path) for solution in solutions),
|
84
|
+
sanitized=sanitized,
|
83
85
|
)
|
84
86
|
if progress:
|
85
87
|
progress.update('Compiling finders...')
|
rbx/box/validators.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
import pathlib
|
2
2
|
import shlex
|
3
|
-
from typing import Dict, List, Optional, Set, Tuple
|
3
|
+
from typing import Dict, Iterable, List, Optional, Set, Tuple
|
4
4
|
|
5
5
|
import typer
|
6
6
|
from pydantic import BaseModel
|
7
7
|
|
8
8
|
from rbx import console
|
9
9
|
from rbx.box import package
|
10
|
-
from rbx.box.code import compile_item, run_item
|
10
|
+
from rbx.box.code import SanitizationLevel, compile_item, run_item
|
11
11
|
from rbx.box.schema import CodeItem, Primitive
|
12
12
|
from rbx.box.testcases import find_built_testcase_inputs
|
13
13
|
from rbx.grading.judge.sandbox import SandboxBase
|
@@ -32,7 +32,7 @@ class TestcaseValidationInfo(BaseModel):
|
|
32
32
|
|
33
33
|
def _compile_validator(validator: CodeItem) -> str:
|
34
34
|
try:
|
35
|
-
digest = compile_item(validator)
|
35
|
+
digest = compile_item(validator, sanitized=SanitizationLevel.PREFER)
|
36
36
|
except:
|
37
37
|
console.console.print(
|
38
38
|
f'[error]Failed compiling validator [item]{validator.path}[/item][/error]'
|
@@ -65,7 +65,7 @@ def _process_bounds(log: str) -> HitBounds:
|
|
65
65
|
return bounds
|
66
66
|
|
67
67
|
|
68
|
-
def _merge_hit_bounds(hit_bounds:
|
68
|
+
def _merge_hit_bounds(hit_bounds: Iterable[HitBounds]) -> HitBounds:
|
69
69
|
res: HitBounds = {}
|
70
70
|
for hb in hit_bounds:
|
71
71
|
for k, hit in hb.items():
|
@@ -241,7 +241,7 @@ def has_validation_errors(infos: List[TestcaseValidationInfo]) -> bool:
|
|
241
241
|
|
242
242
|
def print_validation_report(infos: List[TestcaseValidationInfo]):
|
243
243
|
console.console.rule('Validation report', style='status')
|
244
|
-
hit_bounds_per_group: Dict[str, HitBounds] = {}
|
244
|
+
hit_bounds_per_group: Dict[Optional[str], HitBounds] = {}
|
245
245
|
for info in infos:
|
246
246
|
if not info.ok:
|
247
247
|
console.console.print(
|
rbx/config.py
CHANGED
rbx/grading/caching.py
CHANGED
@@ -334,6 +334,10 @@ class DependencyCache:
|
|
334
334
|
for logs, reference_logs in zip(fingerprint.logs, reference_fingerprint.logs):
|
335
335
|
if logs.run is not None:
|
336
336
|
reference_logs.run = logs.run.model_copy(deep=True)
|
337
|
+
if logs.preprocess is not None:
|
338
|
+
reference_logs.preprocess = [
|
339
|
+
log.model_copy(deep=True) for log in logs.preprocess
|
340
|
+
]
|
337
341
|
reference_logs.cached = True
|
338
342
|
|
339
343
|
return True
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import importlib
|
4
4
|
import importlib.resources
|
5
5
|
import logging
|
6
|
+
import os
|
6
7
|
import pathlib
|
7
8
|
import shutil
|
8
9
|
import signal
|
@@ -291,6 +292,7 @@ class StupidSandbox(SandboxBase):
|
|
291
292
|
stdin=subprocess.PIPE,
|
292
293
|
stdout=subprocess.PIPE,
|
293
294
|
stderr=subprocess.STDOUT,
|
295
|
+
env={**os.environ, **self.params.set_env},
|
294
296
|
)
|
295
297
|
self.hydrate_logs()
|
296
298
|
return self.translate_box_exitcode(self.returncode)
|
@@ -200,14 +200,14 @@ def main():
|
|
200
200
|
os.kill(sub_pid, 9)
|
201
201
|
return
|
202
202
|
|
203
|
-
signal.
|
203
|
+
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
204
204
|
|
205
|
-
signal.
|
205
|
+
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
206
206
|
signal.signal(signal.SIGALRM, handle_alarm)
|
207
207
|
wait_and_finish(sub_pid, options, start_time, alarm_msg=alarm_msg)
|
208
208
|
|
209
209
|
# Cancel alarm before exiting to avoid surprises.
|
210
|
-
signal.
|
210
|
+
signal.setitimer(signal.ITIMER_REAL, 0)
|
211
211
|
|
212
212
|
# Exit gracefully.
|
213
213
|
sys.exit()
|
rbx/grading/steps.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
import functools
|
2
|
-
import os
|
3
2
|
import pathlib
|
3
|
+
import re
|
4
4
|
import shlex
|
5
5
|
import shutil
|
6
6
|
import subprocess
|
7
|
+
import sys
|
7
8
|
from enum import Enum
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
9
|
+
from typing import IO, Any, Dict, List, Optional, Tuple, Union
|
9
10
|
|
11
|
+
import typer
|
10
12
|
from pydantic import BaseModel
|
11
13
|
from rich.text import Text
|
12
14
|
|
@@ -14,7 +16,7 @@ from rbx import utils
|
|
14
16
|
from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
|
15
17
|
from rbx.console import console
|
16
18
|
from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
|
17
|
-
from rbx.grading.judge.storage import copyfileobj
|
19
|
+
from rbx.grading.judge.storage import Storage, copyfileobj
|
18
20
|
|
19
21
|
MAX_STDOUT_LEN = 1024 * 1024 * 128 # 128 MB
|
20
22
|
|
@@ -36,6 +38,7 @@ class DigestHolder(BaseModel):
|
|
36
38
|
|
37
39
|
class GradingLogsHolder(BaseModel):
|
38
40
|
run: Optional['RunLog'] = None
|
41
|
+
preprocess: Optional[List['PreprocessLog']] = None
|
39
42
|
cached: bool = False
|
40
43
|
|
41
44
|
|
@@ -114,6 +117,17 @@ class GradingFileOutput(BaseModel):
|
|
114
117
|
# Whether to track file through its hash (disable for optimization).
|
115
118
|
hash: bool = True
|
116
119
|
|
120
|
+
def get_file(self, storage: Storage) -> Optional[IO[bytes]]:
|
121
|
+
if self.dest is not None:
|
122
|
+
if self.optional and not self.dest.exists():
|
123
|
+
return None
|
124
|
+
return self.dest.open('rb')
|
125
|
+
if self.digest is not None and self.digest.value is not None:
|
126
|
+
if self.optional and not storage.exists(self.digest.value):
|
127
|
+
return None
|
128
|
+
return storage.get_file(self.digest.value)
|
129
|
+
raise ValueError('No file to get')
|
130
|
+
|
117
131
|
|
118
132
|
class GradingArtifacts(BaseModel):
|
119
133
|
# Root directory for the produced artifacts.
|
@@ -125,6 +139,18 @@ class GradingArtifacts(BaseModel):
|
|
125
139
|
# Capture certain logs of the execution.
|
126
140
|
logs: Optional[GradingLogsHolder] = None
|
127
141
|
|
142
|
+
def get_input_file_for_dest(self, dest: pathlib.Path) -> Optional[GradingFileInput]:
|
143
|
+
for input in self.inputs:
|
144
|
+
if input.dest == dest:
|
145
|
+
return input
|
146
|
+
return None
|
147
|
+
|
148
|
+
def get_output_file_for_src(self, src: pathlib.Path) -> Optional[GradingFileOutput]:
|
149
|
+
for output in self.outputs:
|
150
|
+
if output.src == src:
|
151
|
+
return output
|
152
|
+
return None
|
153
|
+
|
128
154
|
|
129
155
|
class TestcaseIO(BaseModel):
|
130
156
|
index: int
|
@@ -134,6 +160,7 @@ class TestcaseIO(BaseModel):
|
|
134
160
|
|
135
161
|
class RunLogMetadata(BaseModel):
|
136
162
|
language: Optional[str] = None
|
163
|
+
is_sanitized: bool = False
|
137
164
|
|
138
165
|
|
139
166
|
class RunLog(BaseModel):
|
@@ -141,6 +168,7 @@ class RunLog(BaseModel):
|
|
141
168
|
exitstatus: str = SandboxBase.EXIT_SANDBOX_ERROR
|
142
169
|
time: Optional[float] = 0.0
|
143
170
|
memory: Optional[int] = 0
|
171
|
+
warnings: bool = False
|
144
172
|
metadata: Optional[RunLogMetadata] = None
|
145
173
|
|
146
174
|
def get_run_language(self) -> Optional[str]:
|
@@ -160,6 +188,9 @@ class PreprocessLog(RunLog):
|
|
160
188
|
cmd: List[str]
|
161
189
|
log: str
|
162
190
|
|
191
|
+
def get_command(self) -> str:
|
192
|
+
return ' '.join(self.cmd)
|
193
|
+
|
163
194
|
|
164
195
|
class TestcaseLog(RunLog):
|
165
196
|
stdout_absolute_path: Optional[pathlib.Path] = None
|
@@ -171,6 +202,7 @@ class CheckerResult(BaseModel):
|
|
171
202
|
outcome: Outcome
|
172
203
|
message: str = ''
|
173
204
|
no_tle_outcome: Optional[Outcome] = None
|
205
|
+
sanitizer_warnings: bool = False
|
174
206
|
|
175
207
|
|
176
208
|
class Evaluation(BaseModel):
|
@@ -260,11 +292,25 @@ def _split_and_expand(command: str, sandbox: SandboxBase) -> List[str]:
|
|
260
292
|
|
261
293
|
|
262
294
|
def _is_c_command(exe_command: str) -> bool:
|
263
|
-
return
|
295
|
+
return 'gcc' in exe_command or 'clang' in exe_command
|
264
296
|
|
265
297
|
|
266
298
|
def _is_cpp_command(exe_command: str) -> bool:
|
267
|
-
return
|
299
|
+
return 'g++' in exe_command or 'clang++' in exe_command
|
300
|
+
|
301
|
+
|
302
|
+
def is_cxx_command(exe_command: str) -> bool:
|
303
|
+
return _is_cpp_command(exe_command) or _is_c_command(exe_command)
|
304
|
+
|
305
|
+
|
306
|
+
def is_cxx_sanitizer_command(command: str) -> bool:
|
307
|
+
cmds = shlex.split(command)
|
308
|
+
if not cmds:
|
309
|
+
return False
|
310
|
+
exe = cmds[0]
|
311
|
+
if not is_cxx_command(exe):
|
312
|
+
return False
|
313
|
+
return 'fsanitize' in command
|
268
314
|
|
269
315
|
|
270
316
|
@functools.cache
|
@@ -279,27 +325,35 @@ def _complain_about_clang() -> None:
|
|
279
325
|
)
|
280
326
|
|
281
327
|
|
282
|
-
def
|
328
|
+
def _get_cxx_version_output(command: str) -> Optional[str]:
|
283
329
|
cmds = shlex.split(command)
|
284
330
|
if not cmds:
|
285
331
|
return None
|
286
332
|
exe = cmds[0]
|
287
|
-
|
288
|
-
if not _is_cpp_command(exe):
|
333
|
+
if not is_cxx_command(exe):
|
289
334
|
return None
|
290
335
|
|
336
|
+
exe = cmds[0]
|
291
337
|
output = subprocess.run([exe, '-v'], capture_output=True)
|
292
338
|
if output.returncode != 0:
|
293
|
-
console.print('[error]Failed to get
|
339
|
+
console.print('[error]Failed to get C/C++ compiler version.[/error]')
|
294
340
|
return None
|
295
|
-
|
341
|
+
return output.stderr.decode()
|
342
|
+
|
343
|
+
|
344
|
+
def _maybe_get_bits_stdcpp_for_clang(command: str) -> Optional[GradingFileInput]:
|
345
|
+
version_output = _get_cxx_version_output(command)
|
346
|
+
if version_output is None:
|
347
|
+
return None
|
348
|
+
lines = version_output.splitlines()
|
296
349
|
if not lines:
|
297
350
|
return None
|
298
351
|
# Check the first line for `clang`.
|
299
352
|
if 'clang' not in lines[0]:
|
300
353
|
return None
|
301
354
|
|
302
|
-
|
355
|
+
if not is_cxx_sanitizer_command(command):
|
356
|
+
_complain_about_clang()
|
303
357
|
bits = get_bits_stdcpp()
|
304
358
|
return GradingFileInput(src=bits, dest=pathlib.Path('bits/stdc++.h'))
|
305
359
|
|
@@ -316,10 +370,6 @@ def _maybe_get_bits_stdcpp_for_commands(
|
|
316
370
|
|
317
371
|
@functools.cache
|
318
372
|
def _try_following_alias_for_exe(exe: str) -> Optional[str]:
|
319
|
-
if _is_c_command(exe) and os.environ.get('RBX_C_PATH'):
|
320
|
-
return os.environ['RBX_C_PATH']
|
321
|
-
if _is_cpp_command(exe) and os.environ.get('RBX_CXX_PATH'):
|
322
|
-
return os.environ['RBX_CXX_PATH']
|
323
373
|
output = subprocess.run(
|
324
374
|
f'which {exe}', shell=True, executable=shutil.which('bash'), capture_output=True
|
325
375
|
)
|
@@ -346,6 +396,74 @@ def _try_following_alias_for_commands(commands: List[str]) -> List[str]:
|
|
346
396
|
return res
|
347
397
|
|
348
398
|
|
399
|
+
@functools.cache
|
400
|
+
def _maybe_complain_about_sanitization(command: str) -> None:
|
401
|
+
if not is_cxx_sanitizer_command(command):
|
402
|
+
return
|
403
|
+
if sys.platform != 'darwin':
|
404
|
+
return
|
405
|
+
|
406
|
+
version_output = _get_cxx_version_output(command)
|
407
|
+
if version_output is None:
|
408
|
+
return
|
409
|
+
lines = version_output.splitlines()
|
410
|
+
if not lines:
|
411
|
+
return
|
412
|
+
if 'gcc' in lines[-1]:
|
413
|
+
console.print(
|
414
|
+
'[error]Notice you are using sanitizers in [item]MacOS[/item], but your C/C++ compiler is [item]gcc[/item].[/error]'
|
415
|
+
)
|
416
|
+
console.print('[error]GCC does not support sanitization in MacOS.[/error]')
|
417
|
+
console.print(
|
418
|
+
'[warning]See [item]https://rsalesc.github.io/rbx/cpp-on-macos[/item] for instructions on how to use C/C++ sanitizers on MacOS.[/warning]'
|
419
|
+
)
|
420
|
+
raise typer.Exit(1)
|
421
|
+
|
422
|
+
|
423
|
+
def _check_for_sanitizer_warnings_in_line(line: str) -> bool:
|
424
|
+
line = line.lower()
|
425
|
+
return 'runtime error:' in line or '==error' in line
|
426
|
+
|
427
|
+
|
428
|
+
def _check_for_sanitizer_warnings(
|
429
|
+
sandbox: SandboxBase, stderr_file: Optional[pathlib.Path]
|
430
|
+
) -> bool:
|
431
|
+
if stderr_file is None:
|
432
|
+
return False
|
433
|
+
if not sandbox.file_exists(stderr_file):
|
434
|
+
return False
|
435
|
+
with sandbox.get_file(stderr_file) as f:
|
436
|
+
return any(_check_for_sanitizer_warnings_in_line(line.decode()) for line in f)
|
437
|
+
|
438
|
+
|
439
|
+
_WARNING_RE = re.compile(r'[^:]+:\d+:\d+:[ ]+warning:.*')
|
440
|
+
|
441
|
+
|
442
|
+
def _check_for_compilation_warnings_in_line(line: str) -> bool:
|
443
|
+
if line.startswith('./'):
|
444
|
+
return False
|
445
|
+
matched = _WARNING_RE.match(line) is not None
|
446
|
+
# if matched:
|
447
|
+
# console.print(
|
448
|
+
# '[warning]Compilation warning:[/warning]',
|
449
|
+
# utils.highlight_json_obj(line),
|
450
|
+
# )
|
451
|
+
return matched
|
452
|
+
|
453
|
+
|
454
|
+
def _check_for_compilation_warnings(
|
455
|
+
sandbox: SandboxBase, stderr_file: Optional[pathlib.Path]
|
456
|
+
) -> bool:
|
457
|
+
if stderr_file is None:
|
458
|
+
return False
|
459
|
+
if not sandbox.file_exists(stderr_file):
|
460
|
+
return False
|
461
|
+
with sandbox.get_file(stderr_file) as f:
|
462
|
+
return any(
|
463
|
+
_check_for_compilation_warnings_in_line(line.strip().decode()) for line in f
|
464
|
+
)
|
465
|
+
|
466
|
+
|
349
467
|
def compile(
|
350
468
|
commands: List[str],
|
351
469
|
params: SandboxParams,
|
@@ -366,6 +484,7 @@ def compile(
|
|
366
484
|
sandbox.set_params(params)
|
367
485
|
|
368
486
|
for i, command in enumerate(commands):
|
487
|
+
_maybe_complain_about_sanitization(command)
|
369
488
|
cmd = _split_and_expand(command, sandbox)
|
370
489
|
stdout_file = pathlib.PosixPath(f'compile-{i}.stdout')
|
371
490
|
stderr_file = pathlib.PosixPath(f'compile-{i}.stderr')
|
@@ -399,6 +518,7 @@ def compile(
|
|
399
518
|
exitstatus=sandbox.get_exit_status(),
|
400
519
|
time=sandbox.get_execution_time(),
|
401
520
|
memory=sandbox.get_memory_used(),
|
521
|
+
warnings=_check_for_compilation_warnings(sandbox, stderr_file),
|
402
522
|
log='\n'.join(std_outputs),
|
403
523
|
)
|
404
524
|
logs.append(log)
|
@@ -406,6 +526,9 @@ def compile(
|
|
406
526
|
if log.exitcode != 0:
|
407
527
|
break
|
408
528
|
|
529
|
+
if artifacts.logs is not None:
|
530
|
+
artifacts.logs.preprocess = logs
|
531
|
+
|
409
532
|
if logs and logs[-1].exitcode != 0:
|
410
533
|
console.print(
|
411
534
|
'[error]FAILED[/error] Preprocessing failed with command',
|
@@ -455,6 +578,11 @@ def run(
|
|
455
578
|
memory=sandbox.get_memory_used(),
|
456
579
|
metadata=metadata,
|
457
580
|
)
|
581
|
+
if metadata is not None and metadata.is_sanitized:
|
582
|
+
run_log.warnings = _check_for_sanitizer_warnings(
|
583
|
+
sandbox,
|
584
|
+
params.stderr_file,
|
585
|
+
)
|
458
586
|
if artifacts.logs is not None:
|
459
587
|
artifacts.logs.run = run_log.model_copy()
|
460
588
|
return run_log
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Whether to enable warnings when running solutions.
|
2
|
+
warnings:
|
3
|
+
enabled: true
|
4
|
+
|
5
|
+
# A list of command substitutions to apply to rbx.
|
6
|
+
# Useful when replacing compilers in OS such as Mac.
|
7
|
+
command_substitutions:
|
8
|
+
g++: g++
|
9
|
+
gcc: gcc
|
10
|
+
java: java
|
11
|
+
javac: javac
|
12
|
+
jar: jar
|
13
|
+
python: python
|
14
|
+
python2: python2
|
15
|
+
python3: python3
|
16
|
+
|
17
|
+
# Whether sanitizers will be enabled by default
|
18
|
+
# when running testlib components.
|
19
|
+
# This flag has no effect on running solutions with
|
20
|
+
# sanitizers. For this, you have to use the `-s` flag in `rbx run`.
|
21
|
+
sanitizers:
|
22
|
+
enabled: false
|
23
|
+
|
24
|
+
# A list of command substitutions to apply to rbx when
|
25
|
+
# sanitizers are enabled.
|
26
|
+
#
|
27
|
+
# This is useful when replacing compilers in OS such as Mac,
|
28
|
+
# since GCC on Mac does not support sanitizers.
|
29
|
+
command_substitutions:
|
30
|
+
g++: clang++
|
31
|
+
gcc: clang
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Whether to enable warnings when running solutions.
|
2
|
+
warnings:
|
3
|
+
enabled: true
|
4
|
+
|
5
|
+
# A list of command substitutions to apply to rbx.
|
6
|
+
# Useful when replacing compilers in OS such as Mac.
|
7
|
+
command_substitutions:
|
8
|
+
g++: g++
|
9
|
+
gcc: gcc
|
10
|
+
java: java
|
11
|
+
javac: javac
|
12
|
+
jar: jar
|
13
|
+
python: python
|
14
|
+
python2: python2
|
15
|
+
python3: python3
|
16
|
+
|
17
|
+
# Whether sanitizers will be enabled by default
|
18
|
+
# when running testlib components.
|
19
|
+
# This flag has no effect on running solutions with
|
20
|
+
# sanitizers. For this, you have to use the `-s` flag in `rbx run`.
|
21
|
+
sanitizers:
|
22
|
+
enabled: false
|
23
|
+
|
24
|
+
# A list of command substitutions to apply to rbx when
|
25
|
+
# sanitizers are enabled.
|
26
|
+
#
|
27
|
+
# This is useful when replacing compilers in OS such as Mac,
|
28
|
+
# since GCC on Mac does not support sanitizers.
|
29
|
+
command_substitutions: {}
|
rbx/utils.py
CHANGED
@@ -9,6 +9,7 @@ from typing import Any, Optional, Type, TypeVar
|
|
9
9
|
import rich
|
10
10
|
import rich.prompt
|
11
11
|
import rich.status
|
12
|
+
import ruyaml
|
12
13
|
import typer
|
13
14
|
import yaml
|
14
15
|
from fastapi.encoders import jsonable_encoder
|
@@ -91,6 +92,12 @@ def validate_field(model: Type[T], field: str, value: Any):
|
|
91
92
|
)
|
92
93
|
|
93
94
|
|
95
|
+
def save_ruyaml(path: pathlib.Path, yml: ruyaml.YAML, data: ruyaml.Any):
|
96
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
97
|
+
with path.open('w') as f:
|
98
|
+
yml.dump(data, f)
|
99
|
+
|
100
|
+
|
94
101
|
def confirm_on_status(status: Optional[rich.status.Status], *args, **kwargs) -> bool:
|
95
102
|
if status:
|
96
103
|
status.stop()
|
@@ -113,7 +120,7 @@ def get_open_fds():
|
|
113
120
|
|
114
121
|
|
115
122
|
@contextlib.contextmanager
|
116
|
-
def new_cd(x):
|
123
|
+
def new_cd(x: pathlib.Path):
|
117
124
|
d = os.getcwd()
|
118
125
|
|
119
126
|
# This could raise an exception, but it's probably
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: rbx.cp
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.18
|
4
4
|
Summary:
|
5
5
|
Author: Roberto Sales
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -27,6 +27,7 @@ Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
27
27
|
Requires-Dist: questionary (>=2.1.0,<3.0.0)
|
28
28
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
29
29
|
Requires-Dist: rich (>=13.9.4,<14.0.0)
|
30
|
+
Requires-Dist: ruyaml (>=0.91.0,<0.92.0)
|
30
31
|
Requires-Dist: textual (>=0.79.1,<0.80.0)
|
31
32
|
Requires-Dist: typer (>=0.15.1,<0.16.0)
|
32
33
|
Description-Content-Type: text/markdown
|