rbx.cp 0.18.2__py3-none-any.whl → 0.18.4__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/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.18.2'
1
+ __version__ = '0.18.4'
rbx/box/cli.py CHANGED
@@ -680,6 +680,12 @@ async def stress(
680
680
  help='Whether to print descriptors of the stress test.',
681
681
  hidden=True,
682
682
  ),
683
+ skip_invalid_testcases: bool = typer.Option(
684
+ False,
685
+ '--skip-invalid',
686
+ '--skip',
687
+ help='Whether to skip invalid testcases.',
688
+ ),
683
689
  ):
684
690
  if finder and not generator_args or generator_args and not finder:
685
691
  console.console.print(
@@ -701,6 +707,7 @@ async def stress(
701
707
  verbose=verbose,
702
708
  sanitized=sanitized,
703
709
  print_descriptors=print_descriptors,
710
+ skip_invalid_testcases=skip_invalid_testcases,
704
711
  )
705
712
 
706
713
  stresses.print_stress_report(report)
rbx/box/code.py CHANGED
@@ -443,17 +443,19 @@ def _precompile_header(
443
443
  )
444
444
 
445
445
  with profiling.PushContext('code.precompile_header'):
446
- if not steps_with_caching.compile(
447
- commands,
448
- params=sandbox_params,
449
- artifacts=precompilation_artifacts,
450
- sandbox=sandbox,
451
- dependency_cache=dependency_cache,
452
- ):
453
- console.console.print(
446
+ try:
447
+ steps_with_caching.compile(
448
+ commands,
449
+ params=sandbox_params,
450
+ artifacts=precompilation_artifacts,
451
+ sandbox=sandbox,
452
+ dependency_cache=dependency_cache,
453
+ )
454
+ except steps.CompilationError as e:
455
+ e.print(
454
456
  f'[error]Failed to precompile header file: [item]{input_artifact.src}[/item][/error]'
455
457
  )
456
- raise typer.Exit(1)
458
+ raise
457
459
 
458
460
  if verbose:
459
461
  console.console.print(
@@ -487,13 +489,14 @@ def _precompile_header(
487
489
  ):
488
490
  input = DigestOrSource.create(local_cacher.put_file_from_fobj(f))
489
491
 
490
- return GradingFileInput(
492
+ res = GradingFileInput(
491
493
  **input.expand(),
492
494
  dest=input_artifact.dest.with_suffix('.h.gch'),
493
495
  # Do not track fingerprint of the precompiled header file,
494
496
  # trust the compilation step above.
495
497
  hash=False,
496
498
  )
499
+ return res
497
500
 
498
501
 
499
502
  # Compile code item and return its digest in the storage.
@@ -609,14 +612,19 @@ def compile_item(
609
612
  grading_context.CacheLevel.NO_CACHE,
610
613
  when=lambda: is_path_remote(code.path),
611
614
  ):
612
- if not steps_with_caching.compile(
613
- commands,
614
- params=sandbox_params,
615
- artifacts=artifacts,
616
- sandbox=sandbox,
617
- dependency_cache=dependency_cache,
618
- ):
619
- raise typer.Exit(1)
615
+ try:
616
+ steps_with_caching.compile(
617
+ commands,
618
+ params=sandbox_params,
619
+ artifacts=artifacts,
620
+ sandbox=sandbox,
621
+ dependency_cache=dependency_cache,
622
+ )
623
+ except steps.CompilationError as e:
624
+ e.print(
625
+ f'[error]Failed to compile item: [item]{code.path}[/item][/error]'
626
+ )
627
+ raise
620
628
 
621
629
  assert compiled_digest.value is not None
622
630
 
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  import pathlib
3
+ import shutil
3
4
  import tempfile
4
5
  import typing
5
6
  from typing import Any, Dict, List, Optional, Tuple
@@ -228,10 +229,10 @@ def _build_problem_statements(
228
229
  )
229
230
  issue_stack.add_issue(StatementBuildIssue(extracted_problem.problem))
230
231
  continue
231
- dest_dir = root / '.problems' / extracted_problem.problem.short_name
232
+ dest_dir = pathlib.Path('.problems') / extracted_problem.problem.short_name
232
233
  dest_path = dest_dir / f'statement{output_type.get_file_suffix()}'
233
- dest_dir.mkdir(parents=True, exist_ok=True)
234
- dest_path.write_bytes(content)
234
+ (root / dest_dir).mkdir(parents=True, exist_ok=True)
235
+ (root / dest_path).write_bytes(content)
235
236
 
236
237
  problem_assets = (
237
238
  get_relative_assets(
@@ -240,7 +241,7 @@ def _build_problem_statements(
240
241
  )
241
242
  + contest_assets
242
243
  )
243
- prepare_assets(problem_assets, dest_dir)
244
+ prepare_assets(problem_assets, root / dest_dir)
244
245
 
245
246
  res.append(dataclasses.replace(extracted_problem, built_statement=dest_path))
246
247
  return res
@@ -393,6 +394,10 @@ def build_statement_rooted(
393
394
  return last_content, last_output
394
395
 
395
396
 
397
+ def get_statement_build_dir(statement: ContestStatement) -> pathlib.Path:
398
+ return pathlib.Path('build') / 'statement_build' / statement.name
399
+
400
+
396
401
  def build_statement(
397
402
  statement: ContestStatement,
398
403
  contest: Contest,
@@ -401,17 +406,18 @@ def build_statement(
401
406
  custom_vars: Optional[Dict[str, Any]] = None,
402
407
  install_tex: bool = False,
403
408
  ) -> pathlib.Path:
404
- with tempfile.TemporaryDirectory() as td:
405
- root = pathlib.Path(td)
406
- last_content, last_output = build_statement_rooted(
407
- statement,
408
- contest,
409
- root,
410
- output_type=output_type,
411
- use_samples=use_samples,
412
- custom_vars=custom_vars,
413
- install_tex=install_tex,
414
- )
409
+ root = get_statement_build_dir(statement)
410
+ shutil.rmtree(root, ignore_errors=True)
411
+ root.mkdir(parents=True, exist_ok=True)
412
+ last_content, last_output = build_statement_rooted(
413
+ statement,
414
+ contest,
415
+ root,
416
+ output_type=output_type,
417
+ use_samples=use_samples,
418
+ custom_vars=custom_vars,
419
+ install_tex=install_tex,
420
+ )
415
421
 
416
422
  statement_path = (pathlib.Path('build') / statement.name).with_suffix(
417
423
  last_output.get_file_suffix()
rbx/box/environment.py CHANGED
@@ -252,7 +252,7 @@ execution config can be individually overridden in the language configuration.""
252
252
 
253
253
 
254
254
  def get_app_environment_path(env: str) -> pathlib.Path:
255
- return config.get_resources_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
255
+ return config.get_resources_file(pathlib.PosixPath('presets') / env / 'env.rbx.yml')
256
256
 
257
257
 
258
258
  def get_active_environment_path() -> pathlib.Path:
@@ -294,11 +294,18 @@ def get_environment(env: Optional[str] = None) -> Environment:
294
294
 
295
295
 
296
296
  @functools.cache
297
- def get_language(name: str) -> EnvironmentLanguage:
297
+ def get_language_or_nil(name: str) -> Optional[EnvironmentLanguage]:
298
298
  for lang in get_environment().languages:
299
299
  if lang.name == name:
300
300
  return lang
301
- console.console.print(f'Language [item]{name}[/item] not found.', style='error')
301
+ return None
302
+
303
+
304
+ def get_language(name: str) -> EnvironmentLanguage:
305
+ lang = get_language_or_nil(name)
306
+ if lang is not None:
307
+ return lang
308
+ console.console.print(f'[error]Language [item]{name}[/item] not found.[/error]')
302
309
  raise typer.Exit()
303
310
 
304
311
 
@@ -315,7 +322,7 @@ def get_language_by_extension(extension: str) -> EnvironmentLanguage:
315
322
  if lang is not None:
316
323
  return lang
317
324
  console.console.print(
318
- f'Language with extension [item]{extension}[/item] not found.', style='error'
325
+ f'[error]Language with extension [item]{extension}[/item] not found.[/error]'
319
326
  )
320
327
  raise typer.Exit()
321
328
 
rbx/box/exception.py ADDED
@@ -0,0 +1,71 @@
1
+ from typing import List, Optional
2
+
3
+ from rich.console import Capture, Console
4
+
5
+ from rbx import console
6
+
7
+
8
+ class PossiblyCapture:
9
+ def __init__(
10
+ self, console: Console, msg: List[str], capture: Optional[Capture] = None
11
+ ):
12
+ self.console = console
13
+ self.msg = msg
14
+ self.capture = capture
15
+ self.actual_capture = None
16
+
17
+ def __enter__(self):
18
+ if self.capture is not None:
19
+ return
20
+ self.actual_capture = self.console.capture()
21
+ self.actual_capture.__enter__()
22
+ return self
23
+
24
+ def __exit__(self, exc_type, exc_value, traceback):
25
+ if self.actual_capture is not None:
26
+ self.actual_capture.__exit__(exc_type, exc_value, traceback)
27
+ self.msg.append(self.actual_capture.get())
28
+ self.actual_capture = None
29
+
30
+
31
+ class RbxException(RuntimeError):
32
+ def __init__(self):
33
+ super().__init__()
34
+ self.msg = []
35
+ self.capture = None
36
+ self.console = console.new_console()
37
+
38
+ def possibly_capture(self):
39
+ return PossiblyCapture(self.console, self.msg, self.capture)
40
+
41
+ def rule(self, *args, **kwargs):
42
+ with self.possibly_capture():
43
+ self.console.rule(*args, **kwargs)
44
+
45
+ def print(self, *args, **kwargs):
46
+ with self.possibly_capture():
47
+ self.console.print(*args, **kwargs)
48
+
49
+ def log(self, *args, **kwargs):
50
+ with self.possibly_capture():
51
+ self.console.log(*args, **kwargs)
52
+
53
+ def __enter__(self):
54
+ capture = self.console.capture()
55
+ capture.__enter__()
56
+ self.capture = capture
57
+ return self
58
+
59
+ def __exit__(self, exc_type, exc_value, traceback):
60
+ if self.capture is not None:
61
+ self.capture.__exit__(exc_type, exc_value, traceback)
62
+ self.msg.append(self.capture.get())
63
+ self.capture = None
64
+ if exc_type is not None:
65
+ return
66
+ raise self
67
+
68
+ def __str__(self) -> str:
69
+ if not self.msg:
70
+ return ''
71
+ return ''.join(self.msg)
rbx/box/generators.py CHANGED
@@ -4,10 +4,12 @@ import shutil
4
4
  from typing import Dict, List, Optional, Set
5
5
 
6
6
  import typer
7
+ from rich.console import Console
7
8
 
8
9
  from rbx import console
9
10
  from rbx.box import checkers, package, testcase_utils, validators
10
11
  from rbx.box.code import SanitizationLevel, compile_item, run_item
12
+ from rbx.box.exception import RbxException
11
13
  from rbx.box.schema import (
12
14
  CodeItem,
13
15
  GeneratorCall,
@@ -37,6 +39,10 @@ from rbx.grading.steps import (
37
39
  from rbx.utils import StatusProgress
38
40
 
39
41
 
42
+ class ValidationError(RbxException):
43
+ pass
44
+
45
+
40
46
  def _compile_generator(generator: CodeItem) -> str:
41
47
  return compile_item(generator, sanitized=SanitizationLevel.PREFER)
42
48
 
@@ -261,7 +267,7 @@ async def generate_standalone(
261
267
  validator_digest: Optional[str] = None,
262
268
  progress: Optional[StatusProgress] = None,
263
269
  ):
264
- def _print_error_header(text: Optional[str] = None):
270
+ def _print_error_header(console: Console, text: Optional[str] = None):
265
271
  prefix = 'Failed generating test'
266
272
  if group_entry is not None:
267
273
  prefix += (
@@ -271,11 +277,11 @@ async def generate_standalone(
271
277
  if text:
272
278
  suffix = f': {text}'
273
279
  if spec.generator_call is not None:
274
- console.console.print(
280
+ console.print(
275
281
  f'[error]{prefix} using generator call [info]{spec.generator_call.name} {spec.generator_call.args}[/info]{suffix}[/error]'
276
282
  )
277
283
  else:
278
- console.console.print(f'[error]{prefix}{suffix}[/error]')
284
+ console.print(f'[error]{prefix}{suffix}[/error]')
279
285
 
280
286
  if spec.generator_call is not None:
281
287
  call = spec.generator_call
@@ -333,12 +339,12 @@ async def generate_standalone(
333
339
  validator_digest,
334
340
  )
335
341
  if not validation_info.ok:
336
- _print_error_header('failed validating testcase.')
337
- console.console.print(f'[error]Message:[/error] {validation_info.message}')
338
- console.console.print(
339
- f'Testcase written at [item]{spec.copied_to.inputPath}[/item]'
340
- )
341
- raise typer.Exit(1)
342
+ with ValidationError() as err:
343
+ _print_error_header(err.console, 'failed validating testcase.')
344
+ err.print(f'[error]Message:[/error] {validation_info.message}')
345
+ err.print(
346
+ f'Testcase written at [item]{spec.copied_to.inputPath}[/item]'
347
+ )
342
348
 
343
349
 
344
350
  async def generate_testcases(
rbx/box/main.py CHANGED
@@ -1,6 +1,17 @@
1
1
  # flake8: noqa
2
2
  import nest_asyncio
3
+ import sys
4
+
5
+ from rbx.box.exception import RbxException
3
6
 
4
7
  nest_asyncio.apply()
5
8
 
6
- from rbx.box.cli import app
9
+ from rbx.box.cli import app as app_cli
10
+
11
+
12
+ def app():
13
+ try:
14
+ app_cli()
15
+ except RbxException as e:
16
+ print(str(e))
17
+ sys.exit(1)
rbx/box/package.py CHANGED
@@ -356,7 +356,7 @@ def get_solutions(root: pathlib.Path = pathlib.Path()) -> List[Solution]:
356
356
  relative_file = file.relative_to(root)
357
357
  if (
358
358
  entry.language is not None
359
- or get_language_by_extension(relative_file.suffix) is not None
359
+ or get_language_by_extension(relative_file.suffix[1:]) is not None
360
360
  ):
361
361
  add_solution(
362
362
  Solution.model_copy(
@@ -5,12 +5,18 @@ from rbx.box.packaging.boca.extension import BocaLanguage, BocaLanguageExtension
5
5
 
6
6
 
7
7
  def get_rbx_language_from_boca_language(boca_language: BocaLanguage) -> str:
8
+ # First by BOCA language name.
8
9
  for language in get_environment().languages:
9
10
  language_extension = language.get_extension_or_default(
10
- language.name, BocaLanguageExtension
11
+ 'boca', BocaLanguageExtension
11
12
  )
12
13
  if language_extension.bocaLanguage == boca_language:
13
14
  return language.name
15
+ # Then by rbx language extension.
16
+ for language in get_environment().languages:
17
+ if language.extension == boca_language:
18
+ return language.name
19
+ # Then by rbx language name.
14
20
  return boca_language
15
21
 
16
22
 
rbx/box/solutions.py CHANGED
@@ -55,7 +55,7 @@ from rbx.box.testcase_utils import (
55
55
  parse_interaction,
56
56
  print_interaction,
57
57
  )
58
- from rbx.grading import grading_context
58
+ from rbx.grading import grading_context, steps
59
59
  from rbx.grading.limits import Limits
60
60
  from rbx.grading.steps import (
61
61
  Evaluation,
@@ -90,6 +90,7 @@ class SolutionReportSkeleton(BaseModel):
90
90
  entries: List[TestcaseEntry]
91
91
  groups: List[GroupSkeleton]
92
92
  limits: Dict[str, Limits]
93
+ compiled_solutions: Dict[str, str]
93
94
  verification: VerificationLevel
94
95
  capture_pipes: bool = False
95
96
 
@@ -125,6 +126,9 @@ class SolutionReportSkeleton(BaseModel):
125
126
  return i
126
127
  return None
127
128
 
129
+ def get_solution_compiled_digest(self, solution: Solution) -> str:
130
+ return self.compiled_solutions[str(solution.path)]
131
+
128
132
  def get_solution_path_set(self) -> Set[str]:
129
133
  return set(str(sol.path) for sol in self.solutions)
130
134
 
@@ -179,10 +183,22 @@ def get_exact_matching_solutions(expected_outcome: ExpectedOutcome) -> List[Solu
179
183
  return res
180
184
 
181
185
 
186
+ class FailedToCompileSolutionIssue(issue_stack.Issue):
187
+ def __init__(self, solution: Solution):
188
+ self.solution = solution
189
+
190
+ def get_detailed_section(self) -> Tuple[str, ...]:
191
+ return ('solutions',)
192
+
193
+ def get_detailed_message(self) -> str:
194
+ return f'[item]{href(self.solution.path)}[/item] could not be compiled and was skipped.'
195
+
196
+
182
197
  def compile_solutions(
183
198
  progress: Optional[StatusProgress] = None,
184
199
  tracked_solutions: Optional[Set[str]] = None,
185
200
  sanitized: bool = False,
201
+ fail_if_one: bool = True,
186
202
  ) -> Dict[pathlib.Path, str]:
187
203
  compiled_solutions = {}
188
204
 
@@ -199,6 +215,10 @@ def compile_solutions(
199
215
  if sanitized
200
216
  else SanitizationLevel.NONE,
201
217
  )
218
+ except steps.CompilationError:
219
+ if fail_if_one and len(tracked_solutions) <= 1:
220
+ raise
221
+ issue_stack.add_issue(FailedToCompileSolutionIssue(solution))
202
222
  except:
203
223
  console.console.print(
204
224
  f'[error]Failed compiling solution {href(solution.path)}.[/error]'
@@ -283,13 +303,48 @@ def _get_solutions_for_skeleton(
283
303
  return solutions
284
304
 
285
305
 
306
+ def _get_compiled_solutions_for_skeleton(
307
+ tracked_solutions: Optional[Iterable[str]] = None,
308
+ progress: Optional[StatusProgress] = None,
309
+ sanitized: bool = False,
310
+ verification: VerificationLevel = VerificationLevel.NONE,
311
+ ) -> Tuple[List[Solution], Dict[str, str]]:
312
+ solutions_to_compile = _get_solutions_for_skeleton(tracked_solutions, verification)
313
+
314
+ compiled_solutions = compile_solutions(
315
+ progress=progress,
316
+ tracked_solutions=set(str(solution.path) for solution in solutions_to_compile),
317
+ sanitized=sanitized,
318
+ )
319
+
320
+ # TODO: Handle solutions that failed to compile.
321
+ solutions = [
322
+ solution
323
+ for solution in solutions_to_compile
324
+ if solution.path in compiled_solutions
325
+ ]
326
+
327
+ return solutions, {
328
+ str(solution_path): digest
329
+ for solution_path, digest in compiled_solutions.items()
330
+ }
331
+
332
+
286
333
  def _get_report_skeleton(
287
334
  tracked_solutions: Optional[Iterable[str]] = None,
288
335
  verification: VerificationLevel = VerificationLevel.NONE,
289
336
  timelimit_override: Optional[int] = None,
337
+ progress: Optional[StatusProgress] = None,
338
+ sanitized: bool = False,
290
339
  ) -> SolutionReportSkeleton:
291
340
  pkg = package.find_problem_package_or_die()
292
- solutions = _get_solutions_for_skeleton(tracked_solutions, verification)
341
+
342
+ solutions, compiled_solutions = _get_compiled_solutions_for_skeleton(
343
+ tracked_solutions=tracked_solutions,
344
+ verification=verification,
345
+ progress=progress,
346
+ sanitized=sanitized,
347
+ )
293
348
 
294
349
  langs = set(find_language_name(solution) for solution in solutions)
295
350
  limits = {
@@ -324,6 +379,7 @@ def _get_report_skeleton(
324
379
  groups=groups,
325
380
  limits=limits,
326
381
  entries=entries,
382
+ compiled_solutions=compiled_solutions,
327
383
  verification=verification,
328
384
  capture_pipes=state.STATE.debug_logs,
329
385
  )
@@ -340,7 +396,6 @@ def _produce_solution_items(
340
396
  verification: VerificationLevel = VerificationLevel.NONE,
341
397
  check: bool = True,
342
398
  timelimit_override: Optional[int] = None,
343
- sanitized: bool = False,
344
399
  nruns: int = 0,
345
400
  ) -> List[EvaluationItem]:
346
401
  pkg = package.find_problem_package_or_die()
@@ -354,12 +409,6 @@ def _produce_solution_items(
354
409
  checker_digest = checkers.compile_checker() if check else None
355
410
  interactor_digest = None
356
411
 
357
- compiled_solutions = compile_solutions(
358
- progress=progress,
359
- tracked_solutions=skeleton.get_solution_path_set(),
360
- sanitized=sanitized,
361
- )
362
-
363
412
  def yield_items(
364
413
  solution: SolutionSkeleton, group_name: str
365
414
  ) -> List[EvaluationItem]:
@@ -367,7 +416,7 @@ def _produce_solution_items(
367
416
  for i, eval in enumerate(
368
417
  _run_solution(
369
418
  solution,
370
- compiled_solutions[solution.path],
419
+ skeleton.get_solution_compiled_digest(solution),
371
420
  checker_digest,
372
421
  solution.runs_dir,
373
422
  group_name,
@@ -425,9 +474,11 @@ def run_solutions(
425
474
  nruns: int = 0,
426
475
  ) -> RunSolutionResult:
427
476
  skeleton = _get_report_skeleton(
428
- tracked_solutions,
477
+ progress=progress,
478
+ tracked_solutions=tracked_solutions,
429
479
  verification=verification,
430
480
  timelimit_override=timelimit_override,
481
+ sanitized=sanitized,
431
482
  )
432
483
  result = RunSolutionResult(
433
484
  skeleton=skeleton,
@@ -437,7 +488,6 @@ def run_solutions(
437
488
  verification=verification,
438
489
  check=check,
439
490
  timelimit_override=timelimit_override,
440
- sanitized=sanitized,
441
491
  nruns=nruns,
442
492
  ),
443
493
  )
@@ -588,7 +638,6 @@ def _run_interactive_solutions(
588
638
  progress: Optional[StatusProgress] = None,
589
639
  verification: VerificationLevel = VerificationLevel.NONE,
590
640
  check: bool = True,
591
- sanitized: bool = False,
592
641
  ) -> Iterator[EvaluationItem]:
593
642
  pkg = package.find_problem_package_or_die()
594
643
 
@@ -599,12 +648,6 @@ def _run_interactive_solutions(
599
648
  checker_digest = checkers.compile_checker() if check else None
600
649
  interactor_digest = None
601
650
 
602
- compiled_solutions = compile_solutions(
603
- progress=progress,
604
- tracked_solutions=skeleton.get_solution_path_set(),
605
- sanitized=sanitized,
606
- )
607
-
608
651
  if progress:
609
652
  progress.update('Running solutions...')
610
653
 
@@ -614,7 +657,7 @@ def _run_interactive_solutions(
614
657
  async def run_fn(solution=solution, output_dir=output_dir):
615
658
  return await run_solution_on_testcase(
616
659
  solution,
617
- compiled_solutions[solution.path],
660
+ skeleton.get_solution_compiled_digest(solution),
618
661
  checker_digest,
619
662
  testcase,
620
663
  output_dir=output_dir,
@@ -632,9 +675,16 @@ def _run_interactive_solutions(
632
675
 
633
676
  def _get_interactive_skeleton(
634
677
  tracked_solutions: Optional[Iterable[str]] = None,
678
+ progress: Optional[StatusProgress] = None,
679
+ sanitized: bool = False,
635
680
  verification: VerificationLevel = VerificationLevel.NONE,
636
681
  ) -> SolutionReportSkeleton:
637
- solutions = _get_solutions_for_skeleton(tracked_solutions, verification)
682
+ solutions, compiled_solutions = _get_compiled_solutions_for_skeleton(
683
+ tracked_solutions,
684
+ verification=verification,
685
+ progress=progress,
686
+ sanitized=sanitized,
687
+ )
638
688
 
639
689
  langs = set(find_language_name(solution) for solution in solutions)
640
690
  limits = {
@@ -660,6 +710,7 @@ def _get_interactive_skeleton(
660
710
  limits=limits,
661
711
  entries=[],
662
712
  verification=verification,
713
+ compiled_solutions=compiled_solutions,
663
714
  capture_pipes=True,
664
715
  )
665
716
 
@@ -682,8 +733,10 @@ async def run_and_print_interactive_solutions(
682
733
  ):
683
734
  pkg = package.find_problem_package_or_die()
684
735
  skeleton = _get_interactive_skeleton(
685
- tracked_solutions,
736
+ tracked_solutions=tracked_solutions,
686
737
  verification=verification,
738
+ sanitized=sanitized,
739
+ progress=progress,
687
740
  )
688
741
 
689
742
  should_cache = testcase_entry is not None
@@ -705,7 +758,6 @@ async def run_and_print_interactive_solutions(
705
758
  progress=progress,
706
759
  verification=verification,
707
760
  check=check,
708
- sanitized=sanitized,
709
761
  )
710
762
 
711
763
  for item in items:
@@ -37,16 +37,17 @@ class Latex:
37
37
  self.latex = latex
38
38
 
39
39
  def build_pdf(self, temp_dir: pathlib.Path) -> LatexResult:
40
- temp_path = temp_dir / 'statement.tex'
40
+ temp_path = pathlib.Path('statement.tex')
41
41
  output_path = temp_path.with_suffix('.pdf')
42
42
  args = ['pdflatex', '-interaction', 'nonstopmode', str(temp_path)]
43
- temp_path.write_text(self.latex)
43
+
44
+ (temp_dir / temp_path).write_text(self.latex)
44
45
 
45
46
  completed = subprocess.run(args, timeout=15, capture_output=True, cwd=temp_dir)
46
- if completed.returncode != 0 or not output_path.exists():
47
+ if completed.returncode != 0 or not (temp_dir / output_path).exists():
47
48
  return LatexResult(result=completed, pdf=None)
48
49
 
49
- return LatexResult(result=completed, pdf=output_path.read_bytes())
50
+ return LatexResult(result=completed, pdf=(temp_dir / output_path).read_bytes())
50
51
 
51
52
 
52
53
  def install_tex_packages(path: pathlib.Path, cwd: pathlib.Path):
rbx/box/stresses.py CHANGED
@@ -14,6 +14,7 @@ from rbx.box import checkers, generators, package, tasks, validators
14
14
  from rbx.box.code import SanitizationLevel, compile_item
15
15
  from rbx.box.generators import (
16
16
  GenerationMetadata,
17
+ ValidationError,
17
18
  expand_generator_call,
18
19
  generate_standalone,
19
20
  )
@@ -34,6 +35,7 @@ class StressFinding(BaseModel):
34
35
  class StressReport(BaseModel):
35
36
  findings: List[StressFinding] = []
36
37
  executed: int = 0
38
+ skipped: int = 0
37
39
 
38
40
 
39
41
  def _compile_finder(finder: CodeItem) -> str:
@@ -57,6 +59,7 @@ async def run_stress(
57
59
  progress: Optional[StatusProgress] = None,
58
60
  sanitized: bool = False,
59
61
  print_descriptors: bool = False,
62
+ skip_invalid_testcases: bool = False,
60
63
  ) -> StressReport:
61
64
  pkg = package.find_problem_package_or_die()
62
65
 
@@ -129,6 +132,7 @@ async def run_stress(
129
132
  startTime = time.monotonic()
130
133
 
131
134
  executed = 0
135
+ skipped = 0
132
136
  findings = []
133
137
 
134
138
  while len(findings) < findingsLimit:
@@ -142,9 +146,11 @@ async def run_stress(
142
146
 
143
147
  if progress:
144
148
  seconds = timeoutInSeconds - int(time.monotonic() - startTime)
149
+ skipped_str = f'skipped [item]{skipped}[/item], ' if skipped else ''
145
150
  progress.update(
146
151
  f'Stress testing: found [item]{len(findings)}[/item] tests, '
147
152
  f'executed [item]{executed}[/item], '
153
+ f'{skipped_str}'
148
154
  f'[item]{seconds}[/item] second(s) remaining...'
149
155
  )
150
156
 
@@ -152,16 +158,26 @@ async def run_stress(
152
158
  input_path.parent.mkdir(parents=True, exist_ok=True)
153
159
 
154
160
  expanded_generator_call = expand_generator_call(stress.generator)
155
- await generate_standalone(
156
- GenerationMetadata(
157
- generator_call=expanded_generator_call,
158
- copied_to=Testcase(inputPath=input_path),
159
- ),
160
- generator_digest=generator_digest,
161
- validator_digest=compiled_validator[1]
162
- if compiled_validator is not None
163
- else None,
164
- )
161
+ try:
162
+ await generate_standalone(
163
+ GenerationMetadata(
164
+ generator_call=expanded_generator_call,
165
+ copied_to=Testcase(inputPath=input_path),
166
+ ),
167
+ generator_digest=generator_digest,
168
+ validator_digest=compiled_validator[1]
169
+ if compiled_validator is not None
170
+ else None,
171
+ )
172
+ except ValidationError as err:
173
+ if skip_invalid_testcases:
174
+ skipped += 1
175
+ continue
176
+ with err:
177
+ err.print('[warning]Invalid testcase generated.[/warning]')
178
+ err.print(
179
+ '[warning]You can use the [item]--skip[/item] flag to skip invalid testcases without halting.[/warning]'
180
+ )
165
181
 
166
182
  @async_lru.alru_cache(maxsize=None)
167
183
  async def run_solution_fn(
@@ -303,12 +319,14 @@ async def run_stress(
303
319
  # Be cooperative.
304
320
  time.sleep(0.001)
305
321
 
306
- return StressReport(findings=findings, executed=executed)
322
+ return StressReport(findings=findings, executed=executed, skipped=skipped)
307
323
 
308
324
 
309
325
  def print_stress_report(report: StressReport):
310
326
  console.console.rule('Stress test report', style='status')
311
327
  console.console.print(f'Executed [item]{report.executed}[/item] tests.')
328
+ if report.skipped:
329
+ console.console.print(f'Skipped [item]{report.skipped}[/item] invalid tests.')
312
330
  if not report.findings:
313
331
  console.console.print('No stress test findings.')
314
332
  return
@@ -9,6 +9,7 @@ class TestingPreset(TestingShared):
9
9
  def __init__(self, root: PathOrStr):
10
10
  super().__init__(root)
11
11
  self._yml = None
12
+ self.initialize()
12
13
 
13
14
  def initialize(self):
14
15
  if not self.yml_path.exists():
@@ -16,6 +16,13 @@ from throttlex import Throttler
16
16
 
17
17
  from rbx import console, utils
18
18
  from rbx.box import naming
19
+ from rbx.box.environment import (
20
+ get_environment,
21
+ get_language_or_nil,
22
+ )
23
+ from rbx.box.packaging.boca.boca_language_utils import (
24
+ get_rbx_language_from_boca_language,
25
+ )
19
26
  from rbx.box.tooling.boca.debug_utils import pretty_print_request_data
20
27
  from rbx.grading.steps import Outcome
21
28
 
@@ -104,6 +111,24 @@ class BocaDetailedRun(BocaRun):
104
111
  autojudge_answer: str
105
112
 
106
113
 
114
+ def get_boca_languages() -> List[BocaLanguage]:
115
+ env = get_environment()
116
+ res = []
117
+ for bocaLanguage in env.extensions.boca.languages:
118
+ rbxLanguage = get_rbx_language_from_boca_language(bocaLanguage)
119
+ rbxLanguage = get_language_or_nil(rbxLanguage)
120
+ if rbxLanguage is None:
121
+ continue
122
+ res.append(
123
+ BocaLanguage(
124
+ index=len(res) + 1,
125
+ name=rbxLanguage.readableName,
126
+ extension=bocaLanguage,
127
+ )
128
+ )
129
+ return res
130
+
131
+
107
132
  class BocaScraper:
108
133
  def __init__(
109
134
  self,
@@ -778,6 +803,35 @@ class BocaScraper:
778
803
  self.open(req)
779
804
  self.log('Main site configured successfully')
780
805
 
806
+ def configure_languages(self, languages: List[BocaLanguage]):
807
+ self.open(
808
+ f'{self.base_url}/admin/language.php',
809
+ error_msg='Error while configuring languages in BOCA',
810
+ )
811
+ try:
812
+ self.br.select_form(name='form1')
813
+ except mechanize.FormNotFoundError:
814
+ self.error(
815
+ 'Languages configuration form not found in BOCA website. This might happen when the login failed.'
816
+ )
817
+
818
+ reqs = []
819
+ for language in languages:
820
+ form = typing.cast(mechanize.HTMLForm, self.br.form)
821
+ form.set_all_readonly(False)
822
+ form['confirmation'] = 'confirm'
823
+
824
+ form['langnumber'] = f'{language.index}'
825
+ form['langname'] = language.name
826
+ form['langextension'] = language.extension
827
+
828
+ reqs.append(self.br.click(name='Submit3', type='submit'))
829
+
830
+ for req in reqs:
831
+ pretty_print_request_data(req)
832
+ self.open(req)
833
+ self.log('Languages configured successfully')
834
+
781
835
  def create_judge_account(self, password: str = 'boca'):
782
836
  _, html = self.open(
783
837
  f'{self.base_url}/admin/user.php',
rbx/console.py CHANGED
@@ -22,6 +22,10 @@ console = Console(theme=theme, style='info', highlight=False)
22
22
  stderr_console = Console(theme=theme, style='info', highlight=False, stderr=True)
23
23
 
24
24
 
25
+ def new_console():
26
+ return Console(theme=theme, style='info', highlight=False)
27
+
28
+
25
29
  def multiline_prompt(text: str) -> str:
26
30
  console.print(f'{text} (Ctrl-D to finish):\n')
27
31
  lines = sys.stdin.readlines()
@@ -20,12 +20,18 @@ with open(extra, 'w') as f:
20
20
  rd = sys.stdin.read(1)
21
21
  if rd == '':
22
22
  break
23
- sys.stdout.write(rd)
24
- sys.stdout.flush()
23
+
24
+ # Write merged capture first.
25
25
  if new:
26
26
  sys.stderr.write(c)
27
27
  sys.stderr.write(rd)
28
28
  sys.stderr.flush()
29
29
 
30
+ # Write to program.
31
+ sys.stdout.write(rd)
32
+ sys.stdout.flush()
33
+
34
+ # Write to extra file kind of
35
+ # asynchronously.
30
36
  f.write(rd)
31
37
  new = rd == '\n'
rbx/grading/steps.py CHANGED
@@ -14,7 +14,8 @@ import typer
14
14
  from pydantic import BaseModel, Field
15
15
  from rich.text import Text
16
16
 
17
- from rbx import testing_utils, utils
17
+ from rbx import utils
18
+ from rbx.box.exception import RbxException
18
19
  from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
19
20
  from rbx.console import console
20
21
  from rbx.grading import grading_context
@@ -672,12 +673,16 @@ def _build_run_log(
672
673
  return run_log
673
674
 
674
675
 
676
+ class CompilationError(RbxException):
677
+ pass
678
+
679
+
675
680
  def compile(
676
681
  commands: List[str],
677
682
  params: SandboxParams,
678
683
  sandbox: SandboxBase,
679
684
  artifacts: GradingArtifacts,
680
- ) -> bool:
685
+ ):
681
686
  sandbox.reset()
682
687
 
683
688
  commands = _try_following_alias_for_commands(commands)
@@ -685,7 +690,7 @@ def compile(
685
690
 
686
691
  if not commands:
687
692
  # Code does not need preprocessing of any kind.
688
- return True
693
+ return
689
694
 
690
695
  logs: List[PreprocessLog] = []
691
696
  params = params.model_copy(deep=True) # Copy to allow further modification.
@@ -731,16 +736,17 @@ def compile(
731
736
  artifacts.logs.preprocess = logs
732
737
 
733
738
  if logs and logs[-1].exitcode != 0:
734
- console.print(
735
- '[error]FAILED[/error] Preprocessing failed with command',
736
- utils.highlight_json_obj(logs[-1].cmd),
737
- )
738
- console.print(f'[error]Summary:[/error] {logs[-1].get_summary()}')
739
- console.print(Text.from_ansi(logs[-1].log), style='default')
740
- testing_utils.print_directory_tree(sandbox.get_root_path())
741
- return False
739
+ with CompilationError() as err:
740
+ err.print(
741
+ '[error]FAILED[/error] Preprocessing failed with command',
742
+ utils.highlight_json_obj(logs[-1].cmd),
743
+ )
744
+ err.print(f'[error]Summary:[/error] {logs[-1].get_summary()}')
745
+ err.print(Text.from_ansi(logs[-1].log), style='default')
742
746
 
743
- return _process_output_artifacts(artifacts, sandbox)
747
+ # testing_utils.print_directory_tree(sandbox.get_root_path())
748
+ if not _process_output_artifacts(artifacts, sandbox):
749
+ raise CompilationError()
744
750
 
745
751
 
746
752
  async def run(
@@ -27,27 +27,31 @@ def compile(
27
27
  ):
28
28
  artifacts.logs = GradingLogsHolder()
29
29
 
30
- ok = True
31
30
  cached_profile = profiling.Profiler('steps.compile[cached]', start=True)
31
+ err: Optional[steps.CompilationError] = None
32
+
32
33
  with dependency_cache(
33
34
  commands, [artifacts], params.get_cacheable_params()
34
35
  ) as is_cached:
35
36
  if not is_cached:
36
37
  with profiling.Profiler('steps.compile'):
37
38
  profiling.add_to_counter('steps.compile')
38
- ok = steps.compile(
39
- commands=commands,
40
- params=params,
41
- artifacts=artifacts,
42
- sandbox=sandbox,
43
- )
44
- if not ok:
45
- raise NoCacheException()
39
+ try:
40
+ steps.compile(
41
+ commands=commands,
42
+ params=params,
43
+ artifacts=artifacts,
44
+ sandbox=sandbox,
45
+ )
46
+ except steps.CompilationError as e:
47
+ err = e
48
+ raise NoCacheException() from None
46
49
  else:
47
50
  cached_profile.stop()
48
51
  profiling.add_to_counter('steps.compile[cached]')
49
52
 
50
- return ok
53
+ if err is not None:
54
+ raise err
51
55
 
52
56
 
53
57
  async def run(
@@ -22,6 +22,9 @@ languages:
22
22
  command: "./{executable}"
23
23
  fileMapping:
24
24
  compilable: "compilable.cpp"
25
+ extensions:
26
+ boca:
27
+ bocaLanguage: "cc"
25
28
  - name: "c"
26
29
  readableName: "C"
27
30
  extension: "c"
@@ -55,7 +58,7 @@ languages:
55
58
  fileMapping:
56
59
  compilable: "Main.java"
57
60
  executable: "Main.jar"
58
- - name: "kotlin"
61
+ - name: "kt"
59
62
  readableName: "Kotlin"
60
63
  extension: "kt"
61
64
  compilation:
@@ -70,8 +73,9 @@ languages:
70
73
  executable: "Main.jar"
71
74
  extensions:
72
75
  boca:
73
- languages: ["c", "cpp", "java", "py3", "kt"]
76
+ languages: ["c", "cc", "java", "py3", "kt"]
74
77
  flags:
75
78
  c: "-O2 -lm -static"
79
+ cc: "-std=c++20 -O2 -lm -static"
76
80
  cpp: "-std=c++20 -O2 -lm -static"
77
81
  preferContestLetter: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  # yaml-language-server: $schema=https://rsalesc.github.io/rbx/schemas/Preset.json
3
3
  name: "default"
4
- min_version: "0.18.2"
4
+ min_version: "0.18.4"
5
5
  uri: "rsalesc/rbx/rbx/resources/presets/default"
6
6
  problem: "problem"
7
7
  contest: "contest"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.18.2
3
+ Version: 0.18.4
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -1,16 +1,16 @@
1
1
  rbx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- rbx/__version__.py,sha256=J_sFuASI-Iw2fcvpgE7ZSHMQFzk4HFwvMA1XLThcK0w,23
2
+ rbx/__version__.py,sha256=9JAsMNke2ijGxNJ6ek-0L9k7GoLH2-C3Oy7ANrPQjCc,23
3
3
  rbx/annotations.py,sha256=_TkLhgZWiUyon3bonHwUo03ls1jY8LwgcR4bVgtgnc0,3519
4
4
  rbx/autoenum.py,sha256=cusv8ClXRlDVvhZ8eDrtYcL_2peXlHugAey_ht8roXk,12025
5
5
  rbx/box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  rbx/box/builder.py,sha256=umrTdVAwvsOosBDVvDZ6kq1yWg3Z2Lxp2o1zK-V7BBk,3594
7
7
  rbx/box/cd.py,sha256=_XAzb3kV1NUaaRs8hc9SGDo10O1yh2_gr1EiAKzfUjI,2711
8
8
  rbx/box/checkers.py,sha256=mRovZyTZdySFEaYFVc3Lc-xgEsu6F9FjVPOxDwub7aY,13226
9
- rbx/box/cli.py,sha256=iNabTCnSf7qhOJAGxUrgWTrPov6xWwq6soMOSoUb9WI,30129
10
- rbx/box/code.py,sha256=iSHIg9wJ2jC7zVOu2l21TcVUip13t_qsgbqwzMNKTRw,25857
9
+ rbx/box/cli.py,sha256=NnOgA0imn17sUYwQfXpJ-34GEq8-wqEKuUen_FaKahY,30358
10
+ rbx/box/code.py,sha256=vBrXjyLht_VGZumbCLkFtIherDIR1WT_YgADwotQKS0,26121
11
11
  rbx/box/compile.py,sha256=Kzn5mEQu4vb91W9vjyt0DS6cfPJzFLTUoowFj7uHLUo,2539
12
12
  rbx/box/contest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- rbx/box/contest/build_contest_statements.py,sha256=UW-Ta-1pTUbMqgy9Drrn4eJj3wdg-KBoqUr1hgaPOrY,14840
13
+ rbx/box/contest/build_contest_statements.py,sha256=M5KTxeuOWmUqKfvCRh_KPNUh4UdptXqqbOMHamPmEwQ,15052
14
14
  rbx/box/contest/contest_package.py,sha256=d7PoUHIWVtqaRGatYOfRvZbQ-bz4MPGmv-H94Eg1yyQ,3289
15
15
  rbx/box/contest/contest_utils.py,sha256=fsWHG1e65wq9zvRY3tdf32VF0nU1yzGTOBX5yjXiNk4,1102
16
16
  rbx/box/contest/main.py,sha256=-aBmlwbEbGMHxWlF7ulIAeKLy2v7VVsvpb4c-kkqKK8,8564
@@ -20,11 +20,12 @@ rbx/box/creation.py,sha256=oTAC11XV2Pw2YAlF_d11Eo7A1fD6ItlpFMpLEzMLyFI,1331
20
20
  rbx/box/deferred.py,sha256=II3X9e87JCOZtmspnHh-n4PFqh-FsH_oc0XJHZ9ZYVQ,691
21
21
  rbx/box/download.py,sha256=tLW5gLVeLk0gHMEMwScSoHIXQPkXuPsqXzItsrsnUZY,3070
22
22
  rbx/box/dump_schemas.py,sha256=HXJ195h4e017DpfoFfW3MRDukON2ZhcKtJjkRPX-dX4,649
23
- rbx/box/environment.py,sha256=RkIIVvn1lyx8WJJ7iBwrv4KeUcWThCka2JCs2RxEolU,14563
23
+ rbx/box/environment.py,sha256=HVqNkK2QHJ4NkbmEABOqwKlzlX-GvvnGPXez9V5EMQw,14738
24
+ rbx/box/exception.py,sha256=pAZH4-yw5SaBj537rck-B0mNTlEF08hrmYnfZOE0XC8,2038
24
25
  rbx/box/extensions.py,sha256=Von8kIeXvNFTkGlMRMTvL2HIHPwlkuiMswr-ydbGV1w,519
25
26
  rbx/box/fields.py,sha256=Gsox7Q1M7I8I2FtKvwiAfYUC6USpwghk8fel2Q9Eyhg,2291
26
27
  rbx/box/formatting.py,sha256=i3vXHpo_L_VpVPxOe4wHlai1WhlDJlfxUexS9DC0Szg,1249
27
- rbx/box/generators.py,sha256=trI4i3BFAzIK4wA0hdCjU1rf8QLwSXKWzSFtCZX17SA,17739
28
+ rbx/box/generators.py,sha256=k7pFcbym3rfH8xVTB6-kqyh6bt_goRD-LfV61pKx1K8,17885
28
29
  rbx/box/git_utils.py,sha256=wuM_k5RgCz5lvoiBciSHo5XT1BdNLhXB0hBlGsc08Vs,2140
29
30
  rbx/box/global_package.py,sha256=YfFGw2vxFspF2v3phBEeBCrLe1sfbANrctGqii3I2X4,2106
30
31
  rbx/box/header.py,sha256=OZJD2J0fdIiYyiEAwQ3x3M8TwM1v1J9nhIjnPVz4Zy8,3029
@@ -32,12 +33,12 @@ rbx/box/lang.py,sha256=CSD-yxFUC3LWdGpv4IVFOLdgONc_JbsI45BEN3bjaFw,888
32
33
  rbx/box/lazy_importing_main.py,sha256=6Z8As7qVFFT619xHH9Xt8VCH57NjC4aDxfAgkWiUwT8,116
33
34
  rbx/box/limits_info.py,sha256=NTtcZTlDzemOtQQXbNbLORpl3RM9nYHc2-cGMnGYfF8,5370
34
35
  rbx/box/linting.py,sha256=wRE0hKCduTBHZYBFmmis_d9AMTsDu0Q-AjByCeTnkrY,3187
35
- rbx/box/main.py,sha256=a8CYi77kOywPFly4-ucEIJLXQW-1NFp91kK2fA42YTE,86
36
+ rbx/box/main.py,sha256=w0Nyyj8Y0eaOg4AhzI6PFITnOZePbkeDHbbGvLLmIrg,264
36
37
  rbx/box/naming.py,sha256=M6aYLIge8PHhIuCX50rwGe5VRzgUL--2BVtymIqeeXs,2389
37
- rbx/box/package.py,sha256=4nbRh1GtTN0SNkPpwJjpl1ngRRtNYudRmYDeFJdamwU,15607
38
+ rbx/box/package.py,sha256=pzViArUFKvQ_S_EWtuigDMZrDx1ymXGqbQ7H9Ieqmik,15611
38
39
  rbx/box/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
40
  rbx/box/packaging/boca/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- rbx/box/packaging/boca/boca_language_utils.py,sha256=bd5-KxN-0h6yg74Jx3NUsgwf_euBU95zC2GLw0MAFS4,1071
41
+ rbx/box/packaging/boca/boca_language_utils.py,sha256=aLL58PLso1zdWuYBvTsFMT_IuJOHtTYe2CYtDQApey8,1300
41
42
  rbx/box/packaging/boca/boca_outcome_utils.py,sha256=MhO7aKTJ1weCgSJBA9MF_ejK3HFBFwC5dIsiR4wA1Mw,310
42
43
  rbx/box/packaging/boca/extension.py,sha256=EQALNEOv4zVDXSKs_dk11n92y7cBZVn8TogIK683lE0,890
43
44
  rbx/box/packaging/boca/packager.py,sha256=8dTGP6RMaWqd3a_3Tl2tEW-OuDgktXYsO8qoSuHHkPY,13909
@@ -63,18 +64,18 @@ rbx/box/sanitizers/issue_stack.py,sha256=nHtF8bksTIbvOlmDEy72SoXnXfQyIDBHU_3NOn-
63
64
  rbx/box/sanitizers/warning_stack.py,sha256=L7tCWmqj2SRtgT7nWPJrKLTqbk4hb5bN9wRKfkCFMWU,2890
64
65
  rbx/box/schema.py,sha256=pbmRiVf8pB6ipA8HCpQer9B9SxpbTvJuCNZswLfWe8g,20314
65
66
  rbx/box/setter_config.py,sha256=vyZbsbgkjelZOblMLX5A1j4CLqPNQkrv83IZr_3-46Y,5301
66
- rbx/box/solutions.py,sha256=DzvGs9olhydtTjb4EMq7HsaH2-99eQtJCXFOqnh-uT4,52866
67
+ rbx/box/solutions.py,sha256=fZMQDgUS4eWRSNDuV92XE9LccnJYHWFH1a1drM5e1Uk,54735
67
68
  rbx/box/state.py,sha256=MMf3DvfQji0jKEliCHct2Tpp_0epL1tvP8HbHNArQIc,166
68
69
  rbx/box/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
70
  rbx/box/statements/build_statements.py,sha256=ZbciKblRUnRnTuuZfqU4W4EK6XhKkxuT1ByGMPjzNQU,13298
70
71
  rbx/box/statements/builders.py,sha256=6849Nku1N0gBYC13wt8Eo6A3s6EfL4hh1VORwrpbEZI,13865
71
72
  rbx/box/statements/expander.py,sha256=sdbMtNcJQCbXGIkFIl9h24pGr77vhFLnM31V5AfuduI,1715
72
73
  rbx/box/statements/joiners.py,sha256=jItNXkAbTjFQpPMgfDMW86n3vMTbaE8sgo9I8Yf4Txg,2886
73
- rbx/box/statements/latex.py,sha256=ipTGjL4kjAsnqgiH6Pk1PwKFegBumQP4-y0pFAbNN8I,1584
74
+ rbx/box/statements/latex.py,sha256=JpPBihuwwgTRHSdULX7gBLA-V245wIy4g57p4ZM9gbQ,1627
74
75
  rbx/box/statements/latex_jinja.py,sha256=UQyD3hOFsiEQdFjqMswRrPXyGCiRPgnzZPmAcvJuPyM,11671
75
76
  rbx/box/statements/schema.py,sha256=zCjpSsq24V179pS7Pti82x4gIpIMogNP6u1EQxdWolE,5529
76
77
  rbx/box/stats.py,sha256=rUAnmp7kTgUvIQ56NLpQaIQkazB37MVcUos5en3xUQw,3258
77
- rbx/box/stresses.py,sha256=2Wefpvfo8iduNjJNyepnxmJci0_u9cGz1nwMfIeASN8,12200
78
+ rbx/box/stresses.py,sha256=H8cNn9MJejZ9PZK-o5WxzaDQlc53_BKO-Fn_TPg2s5E,12994
78
79
  rbx/box/stressing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
80
  rbx/box/stressing/finder_parser.py,sha256=PnONJD2yun5X5EmHqkaz-rh3DHhn_BkTPHZrIXRKZbw,11898
80
81
  rbx/box/stressing/generator_parser.py,sha256=iIWFVPhbdnBZD3zGGDooZ6BWuQ0lRbehqpzc7i_nBeo,7429
@@ -85,7 +86,7 @@ rbx/box/testcases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
85
86
  rbx/box/testcases/main.py,sha256=_I7h_obRcpNLRQ6dDJDIE5NAvTyn5nBOhdsBhRA_PvU,5442
86
87
  rbx/box/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
88
  rbx/box/testing/testing_package.py,sha256=szNc8GcxI0ywCjzV8fOuZh5F0fMtQ6_tLOfivF7hu10,15401
88
- rbx/box/testing/testing_preset.py,sha256=pY8Vh0Y9emZXv6LPqYkXtARCBeTDvKe6c8-CX7mN_t8,5766
89
+ rbx/box/testing/testing_preset.py,sha256=ODY0B8poW1ZvEb1xmxvC1-fny6RcbEjS50PtsTvPd00,5792
89
90
  rbx/box/testing/testing_shared.py,sha256=W4JCUo_tKvGRDSeeUx6THjGkzngSqjQlPjn_FYTa-kU,2720
90
91
  rbx/box/timing.py,sha256=vvghTagSTM9UiL98Tin6MMhDfE_9WMH8Vs-eFryMcIE,10268
91
92
  rbx/box/tooling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -94,7 +95,7 @@ rbx/box/tooling/boca/debug_utils.py,sha256=Rs1ptMWsy32jl_DJZASeOfyqh8HCJdhIXufdb
94
95
  rbx/box/tooling/boca/main.py,sha256=knl1rpaHIwA63KkzMJMZQrejzMpbTPBhYqGx1IpuNm4,289
95
96
  rbx/box/tooling/boca/manual_scrape.py,sha256=iuNtOHrWczdaWNNRMKGYkgr-wSwgtjZedpz5-sX5IRQ,724
96
97
  rbx/box/tooling/boca/scrape.py,sha256=7p4DW0smqX0_6o-C1KFIAve3YI7zfIZkP0Y9kAgHDLE,956
97
- rbx/box/tooling/boca/scraper.py,sha256=Tak3VMMveMu-1OiOiXg1mlL2URjk4lCIPudhCu0Im1o,33742
98
+ rbx/box/tooling/boca/scraper.py,sha256=sVovn0pc8J3Euj8M-MzEX9QtRbmgj6PvchJZ1HbiNFA,35508
98
99
  rbx/box/tooling/converter.py,sha256=cTRUuZ7GToMuAUo6VE7tOtaeNkXHJZE29me0yopvvsY,2544
99
100
  rbx/box/tooling/main.py,sha256=uj6XHjJMF3nyb4enbbTK8XbUAuiRLT_RP1kVwSMqV5E,2003
100
101
  rbx/box/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -124,7 +125,7 @@ rbx/box/ui/widgets/two_sided_test_output_box.py,sha256=L-ORiDwd6CP5DFpavrKGBaX0Z
124
125
  rbx/box/unit.py,sha256=3hqhFBoQQSH76EcCVa87XGVII9JRaz3r_feKldlITsk,7876
125
126
  rbx/box/validators.py,sha256=nugrsZ6NMJa1SCH-VybDTDDf_hLP3tJehksessNv75s,10445
126
127
  rbx/config.py,sha256=Tj0NHSf13fXxbNpif5C4qnaL1k3S-G87OnzuykEAcNQ,8463
127
- rbx/console.py,sha256=X8EJy68OROgh6ao3ZcUjZm5Y56VFMzen58ywAuQ7pAU,990
128
+ rbx/console.py,sha256=RMNkO6vxcRypLvsQYtNpomsyA7n8f_DjH80WNq8e10c,1074
128
129
  rbx/grading/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
130
  rbx/grading/caching.py,sha256=hasSs10PV4J_3e6b3xc7BDCnUNzT9uwrDZcZ_h_hL1I,16951
130
131
  rbx/grading/debug_context.py,sha256=kuAXEI8yRG8xfhS9WKKIRh9X0e5JUD8zvl_cpczJTC8,699
@@ -136,18 +137,17 @@ rbx/grading/judge/program.py,sha256=VJ-ItHGsumAvR7YWYtnmAFairfAIu_ASHfyhu7bqvRw,
136
137
  rbx/grading/judge/sandbox.py,sha256=e9Nstf9EoFGE0OBQgqcz5BD5hYDegIeGP5dWVZSA29Q,21342
137
138
  rbx/grading/judge/sandboxes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
139
  rbx/grading/judge/sandboxes/stupid_sandbox.py,sha256=0Pw-Jvh2A7Jbm5mpx3r4mojP9HgFkDhSREWFuS3ixKs,11765
139
- rbx/grading/judge/sandboxes/tee.py,sha256=fLulB8fV7k5cO-ikRgURpsETrvr6LoUSjGxFM3GZs5U,672
140
+ rbx/grading/judge/sandboxes/tee.py,sha256=lNNjSjOpOx8hta1lAo1beroR5XQOwKbRvAn_QXMLuyM,804
140
141
  rbx/grading/judge/storage.py,sha256=8o3pz0Ansanc5t23XGtshJHCD_q4dIR_XorpeVEvg1g,15621
141
142
  rbx/grading/limits.py,sha256=zCyRTEtUQFd1RKL_fFKl3qSiRSIDCalBaSLlt1yhi0Y,896
142
143
  rbx/grading/profiling.py,sha256=OEdtoAzjYjLfi-QI5Ke7tLZzJeqvGpMB2utQBNuH3E4,3369
143
- rbx/grading/steps.py,sha256=rH0Jhgt26NntdZSHRkZepMsDTFYZBjUnlMC2nxbYZKo,26714
144
- rbx/grading/steps_with_caching.py,sha256=RWHtmlEfszKnKM9ADrfbCUZbxZSTD-BiHV3XSiGW8WY,4797
144
+ rbx/grading/steps.py,sha256=Cy0IvuYLxYCBG8pdd55H_v_8mTq0NqeCiIzySZg0gpg,26841
145
+ rbx/grading/steps_with_caching.py,sha256=zUA_MIxLS0g3VwPMd6cH83TZSDQmt5GlQnxI4zrqHc4,4964
145
146
  rbx/resources/checkers/boilerplate.cpp,sha256=vj1Qjy59JKEzb4ZpaX_MkL1FaZn_tTLZXjrIkP0nGfc,363
146
147
  rbx/resources/checkers/noop.cpp,sha256=aZI6EfZEWq2opbb-kysvl0UeUoYGLU682LccP7cfBG8,214
147
148
  rbx/resources/default_config.json,sha256=8GZVHns4nci0-e5ALk9C1lfO6TO9W2ZlmZtxHkL6ibA,949
148
149
  rbx/resources/default_setter_config.mac.yml,sha256=UWRvdsFp-6I9dU1b1q9hYNAday9TXvyVd_5gquIkcFQ,1438
149
150
  rbx/resources/default_setter_config.yml,sha256=cYTiQqxadRyOmgI9aS6LlG3Cn-azKhOmaZlpZBXvBow,1409
150
- rbx/resources/envs/default.rbx.yml,sha256=quSPG5Xs9KroYLATNLPNtORLGRWtrLLt2Fx81T1enAM,1692
151
151
  rbx/resources/packagers/boca/checker.sh,sha256=kesoIYeCttAtuQBq-7HvfXK8cAFTJhFbqHm9rNd_udg,1272
152
152
  rbx/resources/packagers/boca/compare.sh,sha256=a6AmQqwrIq616DHjp_S2l95wO7UiMVy0LQ9EajZKDDk,1201
153
153
  rbx/resources/packagers/boca/compile/c,sha256=DZ9PWjgDTuauwY4my8Y8WXz0gZCBd4I7n0k0QxBaAUs,4466
@@ -200,8 +200,8 @@ rbx/resources/presets/default/contest/statement/contest.rbx.tex,sha256=Jx6op_WdV
200
200
  rbx/resources/presets/default/contest/statement/info.rbx.tex,sha256=Wz8Tbmi2lPWMgDbJsJolUi1xaQMwSnHmpZ3JwAy_Tb0,998
201
201
  rbx/resources/presets/default/contest/statement/instructions.tex,sha256=JG_eR13ukZgEahrrmrbg40H8cUzpoUE8QLocihN-fZ8,2414
202
202
  rbx/resources/presets/default/contest/statement/logo.png,sha256=RLNYmZoc-BR6AZKkmr4UEg3h01YeFzvy604jMAQC7aA,414485
203
- rbx/resources/presets/default/env.rbx.yml,sha256=TdPa9Q6WHx0nyHTSZmsj55KCt9r--3vOnayxXVh_p8w,2009
204
- rbx/resources/presets/default/preset.rbx.yml,sha256=Rilq-8jBNB4cwstp4Y6Rgjpqo9s6oVkz1wjBSyhfuzQ,520
203
+ rbx/resources/presets/default/env.rbx.yml,sha256=9MaKkSc289LrwATbMgIEFpuUayrJwdFBSw4rWY1TS0c,2098
204
+ rbx/resources/presets/default/preset.rbx.yml,sha256=ew0zCqanJkzhxULKncK9ktvmzqgc9miRj-RHJdCPCnM,520
205
205
  rbx/resources/presets/default/problem/.gitignore,sha256=1rt95y9Q7ZHIQn28JyZQUdD5zkpRosjAl9ZqoQmX2cE,149
206
206
  rbx/resources/presets/default/problem/gens/gen.cpp,sha256=rn6sGRjZ1sFE1Rq02r6488iquY9xTrutcvLv4d1sohA,178
207
207
  rbx/resources/presets/default/problem/manual_tests/samples/000.in,sha256=w66OEtCJGqjUNj8cJrqgImgGVm8W_OlIUtF255ds-ow,4
@@ -223,8 +223,8 @@ rbx/resources/templates/rbx.h,sha256=0AZds9R0PmuPgnlTENb33Y81LW0LlnmOJFaoN8oG3Yo
223
223
  rbx/resources/templates/template.cpp,sha256=xXWpWo7fa7HfmPNqkmHcmv3i46Wm0ZL-gPmkRfGvLn4,317
224
224
  rbx/testing_utils.py,sha256=vNNdaytowJfuopszVHeFzVtHWlPfipPW4zpqCOvdZKU,2908
225
225
  rbx/utils.py,sha256=zOXjEkEN9tlzLliWpssNTSmcvdgveUWDdo427qI3dhM,12225
226
- rbx_cp-0.18.2.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
227
- rbx_cp-0.18.2.dist-info/METADATA,sha256=A3UeZg7n8qgSsohWCVcMw461cAdkR-wIvkuTixkwri4,4845
228
- rbx_cp-0.18.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
229
- rbx_cp-0.18.2.dist-info/entry_points.txt,sha256=Gw2_BZ5Jon61biaH_ETbAQGXy8fR5On9gw2U4A1erpo,40
230
- rbx_cp-0.18.2.dist-info/RECORD,,
226
+ rbx_cp-0.18.4.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
227
+ rbx_cp-0.18.4.dist-info/METADATA,sha256=YpJPh094Q6VOfS32IJFcGX8oLSGbKccxoJHK33Gqk00,4845
228
+ rbx_cp-0.18.4.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
229
+ rbx_cp-0.18.4.dist-info/entry_points.txt,sha256=Gw2_BZ5Jon61biaH_ETbAQGXy8fR5On9gw2U4A1erpo,40
230
+ rbx_cp-0.18.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,67 +0,0 @@
1
- ---
2
- sandbox: "stupid"
3
- defaultCompilation:
4
- sandbox:
5
- maxProcesses: 1000
6
- timeLimit: 50000 # 50 seconds
7
- wallTimeLimit: 50000 # 50 seconds
8
- memoryLimit: 1024 # 1gb
9
- defaultExecution:
10
- sandbox:
11
- # Useful for checkers, validators, etc.
12
- timeLimit: 50000 # 50 seconds
13
- wallTimeLimit: 50000 # 50 seconds
14
- memoryLimit: 1024 # 1gb
15
- languages:
16
- - name: "cpp"
17
- readableName: "C++20"
18
- extension: "cpp"
19
- compilation:
20
- commands: ["g++ -std=c++20 -O2 -o {executable} {compilable}"]
21
- execution:
22
- command: "./{executable}"
23
- fileMapping:
24
- compilable: "compilable.cpp"
25
- extensions:
26
- boca:
27
- bocaLanguage: "cc"
28
- - name: "c"
29
- readableName: "C"
30
- extension: "c"
31
- compilation:
32
- commands: ["gcc -std=gnu11 -O2 -lm -o {executable} {compilable}"]
33
- execution:
34
- command: "./{executable}"
35
- fileMapping:
36
- compilable: "compilable.c"
37
- - name: "py"
38
- readableName: "Python3"
39
- extension: "py"
40
- execution:
41
- command: "python3 {executable}"
42
- fileMapping:
43
- executable: "executable.py"
44
- extensions:
45
- boca:
46
- bocaLanguage: "py3"
47
- - name: "java"
48
- readableName: "Java"
49
- extension: "java"
50
- compilation:
51
- commands:
52
- - "javac -Xlint -encoding UTF-8 {compilable}"
53
- - "jar cvf {executable} @glob:*.class"
54
- execution:
55
- command:
56
- "java -Xss100m -Xmx{{memory}}m -Xms{{initialMemory}}m -cp {executable}
57
- Main"
58
- fileMapping:
59
- compilable: "Main.java"
60
- executable: "Main.jar"
61
- extensions:
62
- boca:
63
- languages: ["c", "cc", "java", "py3"]
64
- flags:
65
- c: "-O2 -lm -static"
66
- cc: "-std=c++20 -O2 -lm -static"
67
- preferContestLetter: true