rbx.cp 0.5.24__py3-none-any.whl → 0.5.26__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/code.py CHANGED
@@ -75,7 +75,7 @@ def is_executable_sanitized(executable: DigestOrSource) -> bool:
75
75
 
76
76
  def add_sanitizer_flags_to_command(command: str) -> str:
77
77
  if is_cxx_command(command):
78
- return command + ' -fsanitize=address,undefined -fno-omit-frame-pointer'
78
+ return command + ' -fsanitize=address,undefined -fno-omit-frame-pointer -g'
79
79
  return command
80
80
 
81
81
 
rbx/box/contest/main.py CHANGED
@@ -147,12 +147,15 @@ def add(path: str, short_name: str, preset: Optional[str] = None):
147
147
 
148
148
  ru, contest = contest_package.get_ruyaml()
149
149
 
150
- contest['problems'].append(
151
- {
152
- 'short_name': short_name,
153
- 'path': path,
154
- }
155
- )
150
+ item = {
151
+ 'short_name': short_name,
152
+ 'path': path,
153
+ }
154
+ if 'problems' not in contest:
155
+ contest['problems'] = [item]
156
+ else:
157
+ contest['problems'].append(item)
158
+
156
159
  dest = find_contest_yaml()
157
160
  assert dest is not None
158
161
  utils.save_ruyaml(dest, ru, contest)
rbx/box/main.py CHANGED
@@ -42,6 +42,7 @@ from rbx.box.solutions import (
42
42
  estimate_time_limit,
43
43
  get_exact_matching_solutions,
44
44
  get_matching_solutions,
45
+ pick_solutions,
45
46
  print_run_report,
46
47
  run_and_print_interactive_solutions,
47
48
  run_solutions,
@@ -143,6 +144,13 @@ def run(
143
144
  '-s',
144
145
  help='Whether to compile the solutions with sanitizers enabled.',
145
146
  ),
147
+ choice: bool = typer.Option(
148
+ False,
149
+ '--choice',
150
+ '--choose',
151
+ '-c',
152
+ help='Whether to pick solutions interactively.',
153
+ ),
146
154
  ):
147
155
  main_solution = package.get_main_solution()
148
156
  if check and main_solution is None:
@@ -174,31 +182,37 @@ def run(
174
182
  if override_tl is None:
175
183
  raise typer.Exit(1)
176
184
 
177
- with utils.StatusProgress('Running solutions...') as s:
178
- if sanitized:
179
- console.console.print(
180
- '[warning]Sanitizers are running, so the time limit for the problem will be dropped, '
181
- 'and the environment default time limit will be used instead.[/warning]'
182
- )
185
+ if sanitized:
186
+ console.console.print(
187
+ '[warning]Sanitizers are running, so the time limit for the problem will be dropped, '
188
+ 'and the environment default time limit will be used instead.[/warning]'
189
+ )
190
+
191
+ tracked_solutions = None
192
+ if outcome is not None:
193
+ tracked_solutions = {
194
+ str(solution.path)
195
+ for solution in get_matching_solutions(ExpectedOutcome(outcome))
196
+ }
197
+ if solution:
198
+ tracked_solutions = {solution}
183
199
 
184
- tracked_solutions = None
185
- if outcome is not None:
186
- tracked_solutions = {
187
- str(solution.path)
188
- for solution in get_matching_solutions(ExpectedOutcome(outcome))
189
- }
190
- if solution:
191
- tracked_solutions = {solution}
200
+ if choice:
201
+ tracked_solutions = set(pick_solutions(tracked_solutions))
202
+ if not tracked_solutions:
203
+ console.console.print('[error]No solutions selected. Exiting.[/error]')
204
+ raise typer.Exit(1)
192
205
 
193
- if sanitized and tracked_solutions is None:
194
- console.console.print(
195
- '[warning]Sanitizers are running, and no solutions were specified to run. Will only run [item]ACCEPTED[/item] solutions.'
196
- )
197
- tracked_solutions = {
198
- str(solution.path)
199
- for solution in get_exact_matching_solutions(ExpectedOutcome.ACCEPTED)
200
- }
206
+ if sanitized and tracked_solutions is None:
207
+ console.console.print(
208
+ '[warning]Sanitizers are running, and no solutions were specified to run. Will only run [item]ACCEPTED[/item] solutions.'
209
+ )
210
+ tracked_solutions = {
211
+ str(solution.path)
212
+ for solution in get_exact_matching_solutions(ExpectedOutcome.ACCEPTED)
213
+ }
201
214
 
215
+ with utils.StatusProgress('Running solutions...') as s:
202
216
  solution_result = run_solutions(
203
217
  progress=s,
204
218
  tracked_solutions=tracked_solutions,
@@ -338,6 +352,13 @@ def irun(
338
352
  '-s',
339
353
  help='Whether to compile the solutions with sanitizers enabled.',
340
354
  ),
355
+ choice: bool = typer.Option(
356
+ False,
357
+ '--choice',
358
+ '--choose',
359
+ '-c',
360
+ help='Whether to pick solutions interactively.',
361
+ ),
341
362
  ):
342
363
  if not print:
343
364
  console.console.print(
@@ -364,6 +385,13 @@ def irun(
364
385
  }
365
386
  if solution:
366
387
  tracked_solutions = {solution}
388
+
389
+ if choice:
390
+ tracked_solutions = set(pick_solutions(tracked_solutions))
391
+ if not tracked_solutions:
392
+ console.console.print('[error]No solutions selected. Exiting.[/error]')
393
+ raise typer.Exit(1)
394
+
367
395
  if sanitized and tracked_solutions is None:
368
396
  console.console.print(
369
397
  '[warning]Sanitizers are running, and no solutions were specified to run. Will only run [item]ACCEPTED[/item] solutions.'
@@ -373,18 +401,20 @@ def irun(
373
401
  for solution in get_exact_matching_solutions(ExpectedOutcome.ACCEPTED)
374
402
  }
375
403
 
376
- asyncio.run(
377
- run_and_print_interactive_solutions(
378
- tracked_solutions=tracked_solutions,
379
- check=check,
380
- verification=VerificationLevel(verification),
381
- generator=generators.get_call_from_string(generator)
382
- if generator is not None
383
- else None,
384
- print=print,
385
- sanitized=sanitized,
404
+ with utils.StatusProgress('Running solutions...') as s:
405
+ asyncio.run(
406
+ run_and_print_interactive_solutions(
407
+ progress=s,
408
+ tracked_solutions=tracked_solutions,
409
+ check=check,
410
+ verification=VerificationLevel(verification),
411
+ generator=generators.get_call_from_string(generator)
412
+ if generator is not None
413
+ else None,
414
+ print=print,
415
+ sanitized=sanitized,
416
+ )
386
417
  )
387
- )
388
418
 
389
419
 
390
420
  @app.command('create, c', help='Create a new problem package.')
@@ -510,6 +540,8 @@ def stress(
510
540
  name=testgroup, generatorScript=CodeItem(path=new_script_path)
511
541
  )
512
542
  ru, problem_yml = package.get_ruyaml()
543
+ if 'testcases' not in problem_yml:
544
+ problem_yml['testcases'] = []
513
545
  problem_yml['testcases'].append(
514
546
  {
515
547
  'name': testgroup,
@@ -551,7 +583,10 @@ def stress(
551
583
  @app.command('compile', help='Compile an asset given its path.')
552
584
  @package.within_problem
553
585
  def compile_command(
554
- path: Annotated[str, typer.Argument(help='Path to the asset to compile.')],
586
+ path: Annotated[
587
+ Optional[str],
588
+ typer.Argument(help='Path to the asset to compile.'),
589
+ ] = None,
555
590
  sanitized: bool = typer.Option(
556
591
  False,
557
592
  '--sanitized',
@@ -565,6 +600,12 @@ def compile_command(
565
600
  help='Whether to compile the asset with warnings enabled.',
566
601
  ),
567
602
  ):
603
+ if path is None:
604
+ path = questionary.path("What's the path to your asset?").ask()
605
+ if path is None:
606
+ console.console.print('[error]No path specified.[/error]')
607
+ raise typer.Exit(1)
608
+
568
609
  compile.any(path, sanitized, warnings)
569
610
 
570
611
 
@@ -68,13 +68,14 @@ def _find_nested_preset(root: pathlib.Path) -> Optional[pathlib.Path]:
68
68
 
69
69
 
70
70
  def _find_local_preset(root: pathlib.Path) -> Optional[pathlib.Path]:
71
+ original_root = root
71
72
  root = root.resolve()
72
73
  problem_yaml_path = root / '.local.rbx' / 'preset.rbx.yml'
73
74
  while root != pathlib.PosixPath('/') and not problem_yaml_path.is_file():
74
75
  root = root.parent
75
76
  problem_yaml_path = root / '.local.rbx' / 'preset.rbx.yml'
76
77
  if not problem_yaml_path.is_file():
77
- return _find_nested_preset(root)
78
+ return _find_nested_preset(original_root)
78
79
  return problem_yaml_path.parent
79
80
 
80
81
 
@@ -326,6 +327,13 @@ def _install(root: pathlib.Path = pathlib.Path(), force: bool = False):
326
327
  console.console.print('[error]Naming a preset "local" is prohibited.[/error]')
327
328
 
328
329
  console.console.print(f'Installing preset [item]{preset.name}[/item]...')
330
+ installation_path = get_preset_installation_path(preset.name)
331
+
332
+ if root.resolve().is_relative_to(installation_path.resolve()):
333
+ console.console.print(
334
+ '[error]Current folder is nested into the preset installation path, cannot install it.[/error]'
335
+ )
336
+ raise typer.Exit(1)
329
337
 
330
338
  if preset.env is not None:
331
339
  console.console.print(
@@ -346,7 +354,6 @@ def _install(root: pathlib.Path = pathlib.Path(), force: bool = False):
346
354
  shutil.copyfile(str(root / preset.env), get_environment_path(preset.name))
347
355
 
348
356
  console.console.print(f'[item]{preset.name}[/item]: Copying preset folder...')
349
- installation_path = get_preset_installation_path(preset.name)
350
357
  installation_path.parent.mkdir(parents=True, exist_ok=True)
351
358
  if installation_path.exists():
352
359
  res = force or rich.prompt.Confirm.ask(
@@ -412,7 +419,7 @@ def generate_lock(
412
419
  )
413
420
 
414
421
 
415
- def _sync(update: bool = False):
422
+ def _sync(try_update: bool = False):
416
423
  preset_lock = get_preset_lock()
417
424
  if preset_lock is None:
418
425
  console.console.print(
@@ -423,18 +430,17 @@ def _sync(update: bool = False):
423
430
  )
424
431
  raise typer.Exit(1)
425
432
 
426
- should_update = update and preset_lock.uri is not None
433
+ should_update = try_update and preset_lock.uri is not None
427
434
  installed_preset = get_installed_preset_or_null(preset_lock.preset_name)
428
435
  if installed_preset is None:
429
- if not update or preset_lock.uri is None:
436
+ if not try_update or preset_lock.uri is None:
430
437
  console.console.print(
431
438
  f'[error]Preset [item]{preset_lock.preset_name}[/item] is not installed. Install it before trying to update.'
432
439
  )
433
440
  raise typer.Exit(1)
434
- should_update = True
435
-
436
- if should_update:
437
- install(uri=preset_lock.uri)
441
+ install(preset_lock.uri)
442
+ elif should_update:
443
+ update(preset_lock.name)
438
444
 
439
445
  _copy_updated_assets(
440
446
  preset_lock.preset_name,
@@ -477,6 +483,13 @@ def update(
477
483
  presets = [name]
478
484
 
479
485
  for preset_name in presets:
486
+ if preset_name == LOCAL:
487
+ if not questionary.confirm(
488
+ 'Updating local preset will remove all custom changes you made to the preset.',
489
+ default=False,
490
+ ).ask():
491
+ continue
492
+
480
493
  preset = get_installed_preset_or_null(preset_name)
481
494
  if preset is None:
482
495
  console.console.print(
@@ -490,6 +503,20 @@ def update(
490
503
  continue
491
504
  install_from_remote(preset.fetch_info, force=True)
492
505
 
506
+ if preset_name == LOCAL:
507
+ # Get global path to the preset.
508
+ preset_path = get_preset_installation_path(preset.name)
509
+ dest_path = '.local.rbx'
510
+ shutil.rmtree(dest_path, ignore_errors=True)
511
+ shutil.copytree(preset_path, dest_path)
512
+ console.console.print(
513
+ '[success]Local preset updated successfully.[/success]'
514
+ )
515
+ else:
516
+ console.console.print(
517
+ f'[success]Preset [item]{preset_name}[/item] updated successfully.[/success]'
518
+ )
519
+
493
520
 
494
521
  @app.command(
495
522
  'sync',
@@ -507,7 +534,7 @@ def sync(
507
534
  ] = False,
508
535
  ):
509
536
  _check_is_valid_package()
510
- _sync(update=update)
537
+ _sync(try_update=update)
511
538
 
512
539
 
513
540
  @app.command(
rbx/box/solutions.py CHANGED
@@ -7,11 +7,13 @@ import shutil
7
7
  from collections.abc import Iterator
8
8
  from typing import Dict, Iterable, List, Optional, Set, Tuple
9
9
 
10
+ import questionary
10
11
  import rich
11
12
  import rich.live
12
13
  import rich.markup
13
14
  import rich.table
14
15
  import rich.text
16
+ import typer
15
17
  from pydantic import BaseModel
16
18
 
17
19
  from rbx import console
@@ -410,6 +412,7 @@ def run_solutions(
410
412
 
411
413
 
412
414
  def _run_interactive_solutions(
415
+ progress: Optional[StatusProgress] = None,
413
416
  tracked_solutions: Optional[Set[str]] = None,
414
417
  verification: VerificationLevel = VerificationLevel.NONE,
415
418
  generator: Optional[GeneratorCall] = None,
@@ -423,7 +426,7 @@ def _run_interactive_solutions(
423
426
 
424
427
  checker_digest = checkers.compile_checker() if check else None
425
428
  compiled_solutions = compile_solutions(
426
- tracked_solutions=tracked_solutions, sanitized=sanitized
429
+ progress=progress, tracked_solutions=tracked_solutions, sanitized=sanitized
427
430
  )
428
431
 
429
432
  main_solution_digest = None
@@ -498,6 +501,7 @@ def _run_interactive_solutions(
498
501
 
499
502
 
500
503
  async def run_and_print_interactive_solutions(
504
+ progress: Optional[StatusProgress] = None,
501
505
  tracked_solutions: Optional[Set[str]] = None,
502
506
  verification: VerificationLevel = VerificationLevel.NONE,
503
507
  generator: Optional[GeneratorCall] = None,
@@ -507,6 +511,7 @@ async def run_and_print_interactive_solutions(
507
511
  ):
508
512
  pkg = package.find_problem_package_or_die()
509
513
  items = _run_interactive_solutions(
514
+ progress=progress,
510
515
  tracked_solutions=tracked_solutions,
511
516
  verification=verification,
512
517
  check=check,
@@ -515,6 +520,9 @@ async def run_and_print_interactive_solutions(
515
520
  print=print,
516
521
  )
517
522
 
523
+ if progress:
524
+ progress.stop()
525
+
518
526
  for item in items:
519
527
  sol = pkg.solutions[item.solution_index]
520
528
  _print_solution_header(sol, console.console, is_irun=True)
@@ -540,6 +548,31 @@ async def run_and_print_interactive_solutions(
540
548
  console.console.print()
541
549
 
542
550
 
551
+ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
552
+ return [
553
+ ('', f'{str(sol.path)} '),
554
+ (f'fg:{sol.outcome.style().replace('lnumber', 'cyan')}', sol.outcome.name),
555
+ ]
556
+
557
+
558
+ def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
559
+ pkg = package.find_problem_package_or_die()
560
+ if tracked_solutions is None:
561
+ tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
562
+
563
+ # Store in a separate list to maintain order with the package declaration.
564
+ choices = [
565
+ questionary.Choice(title=_get_solution_repr(sol), value=str(sol.path))
566
+ for sol in pkg.solutions
567
+ if str(sol.path) in tracked_solutions
568
+ ]
569
+
570
+ picked = questionary.checkbox('Select solutions', choices=choices).ask()
571
+ if picked is None:
572
+ raise typer.Abort()
573
+ return picked
574
+
575
+
543
576
  def get_outcome_style_verdict(outcome: Outcome) -> str:
544
577
  if outcome == Outcome.ACCEPTED:
545
578
  return 'green'
@@ -554,21 +587,25 @@ def get_outcome_style_verdict(outcome: Outcome) -> str:
554
587
  return 'magenta'
555
588
 
556
589
 
557
- def get_testcase_markup_verdict(eval: Evaluation) -> str:
590
+ def get_outcome_markup_verdict(outcome: Outcome) -> str:
558
591
  res = '✓'
559
- if eval.result.outcome != Outcome.ACCEPTED:
592
+ if outcome != Outcome.ACCEPTED:
560
593
  res = '✗'
561
- if eval.result.outcome == Outcome.TIME_LIMIT_EXCEEDED:
594
+ if outcome == Outcome.TIME_LIMIT_EXCEEDED:
562
595
  res = '⧖'
563
- if eval.result.outcome == Outcome.RUNTIME_ERROR:
596
+ if outcome == Outcome.RUNTIME_ERROR:
564
597
  res = '✗'
565
- style = get_outcome_style_verdict(eval.result.outcome)
598
+ style = get_outcome_style_verdict(outcome)
566
599
  res = f'[{style}]{res}[/{style}]'
600
+ return res
601
+
602
+
603
+ def get_testcase_markup_verdict(eval: Evaluation) -> str:
567
604
  # if eval.log.stdout_absolute_path:
568
605
  # output_path = eval.log.stdout_absolute_path.resolve()
569
606
  # output_link = f'file://{output_path}'
570
607
  # res = f'[link={output_link}]{res}[/link]'
571
- return res
608
+ return get_outcome_markup_verdict(eval.result.outcome)
572
609
 
573
610
 
574
611
  def _get_evals_time_in_ms(evals: List[Evaluation]) -> int:
@@ -632,6 +669,10 @@ def get_evals_formatted_memory(evals: List[Evaluation]) -> str:
632
669
  return get_formatted_memory(max_memory)
633
670
 
634
671
 
672
+ def get_worst_outcome(evals: List[Evaluation]) -> Outcome:
673
+ return Outcome.worst_outcome(eval.result.outcome for eval in evals)
674
+
675
+
635
676
  def _print_solution_outcome(
636
677
  solution: Solution,
637
678
  evals: List[Evaluation],
@@ -880,7 +921,11 @@ async def _render_detailed_group_table(
880
921
  solution, non_null_evals, verification
881
922
  )
882
923
  formatted_memory = get_evals_formatted_memory(non_null_evals)
883
- summary_row.append(('', '', formatted_time, '/', formatted_memory, ''))
924
+ worst_outcome = get_worst_outcome(non_null_evals)
925
+ verdict = get_outcome_markup_verdict(worst_outcome)
926
+ summary_row.append(
927
+ ('', verdict, formatted_time, '/', formatted_memory, '')
928
+ )
884
929
  padded_rows.append(summary_row)
885
930
 
886
931
  for row in _render_padded_rows(padded_rows):
@@ -1020,12 +1065,13 @@ async def print_run_report(
1020
1065
  )
1021
1066
  console.print()
1022
1067
 
1023
- ok = ok and _print_solution_outcome(
1068
+ cur_ok = _print_solution_outcome(
1024
1069
  solution,
1025
1070
  solution_evals,
1026
1071
  console,
1027
1072
  verification=verification,
1028
1073
  )
1074
+ ok = ok and cur_ok
1029
1075
  console.print()
1030
1076
 
1031
1077
  await _print_timing(console, result.skeleton, structured_evaluations)
rbx/box/validators.py CHANGED
@@ -262,10 +262,22 @@ def print_validation_report(infos: List[TestcaseValidationInfo]):
262
262
  if not _has_group_specific_validator():
263
263
  hit_bounds_per_group = {None: _merge_hit_bounds(hit_bounds_per_group.values())}
264
264
 
265
+ def _is_hit_bound_good(hit_bounds: HitBounds) -> bool:
266
+ return any(not v[0] or not v[1] for v in hit_bounds.values())
267
+
268
+ # Cleanup entries in hit bounds per group that are totally empty.
269
+ # Also skip samples.
270
+ hit_bounds_per_group = {
271
+ k: v
272
+ for k, v in hit_bounds_per_group.items()
273
+ if _is_hit_bound_good(v) and k != 'samples'
274
+ }
275
+
276
+ if not hit_bounds_per_group:
277
+ console.console.print('[info]No validation issues found.[/info]')
278
+ return
279
+
265
280
  for group, hit_bounds in hit_bounds_per_group.items():
266
- if group == 'samples':
267
- # Skip samples.
268
- continue
269
281
  if group is None:
270
282
  console.console.print('Hit bounds:')
271
283
  else:
rbx/console.py CHANGED
@@ -1,5 +1,7 @@
1
1
  import sys
2
2
 
3
+ import rich
4
+ import rich.markup
3
5
  from rich.console import Console
4
6
  from rich.theme import Theme
5
7
 
@@ -25,3 +27,13 @@ def multiline_prompt(text: str) -> str:
25
27
  lines = sys.stdin.readlines()
26
28
  console.print()
27
29
  return ''.join(lines)
30
+
31
+
32
+ def render_from(r: rich.console.RenderableType) -> str:
33
+ with console.capture() as capture:
34
+ console.print(r)
35
+ return capture.get()
36
+
37
+
38
+ def render_from_markup(markup: str) -> str:
39
+ return render_from(rich.markup.render(markup))
rbx/grading/steps.py CHANGED
@@ -6,7 +6,7 @@ import shutil
6
6
  import subprocess
7
7
  import sys
8
8
  from enum import Enum
9
- from typing import IO, Any, Dict, List, Optional, Tuple, Union
9
+ from typing import IO, Any, Dict, Iterable, List, Optional, Tuple, Union
10
10
 
11
11
  import typer
12
12
  from pydantic import BaseModel
@@ -24,13 +24,20 @@ MAX_STDOUT_LEN = 1024 * 1024 * 128 # 128 MB
24
24
  class Outcome(Enum):
25
25
  ACCEPTED = 'accepted'
26
26
  WRONG_ANSWER = 'wrong-answer'
27
- JUDGE_FAILED = 'judge-failed'
28
- RUNTIME_ERROR = 'runtime-error'
29
- TIME_LIMIT_EXCEEDED = 'time-limit-exceeded'
30
27
  MEMORY_LIMIT_EXCEEDED = 'memory-limit-exceeded'
28
+ TIME_LIMIT_EXCEEDED = 'time-limit-exceeded'
29
+ RUNTIME_ERROR = 'runtime-error'
31
30
  OUTPUT_LIMIT_EXCEEDED = 'output-limit-exceeded'
31
+ JUDGE_FAILED = 'judge-failed'
32
32
  INTERNAL_ERROR = 'internal-error'
33
33
 
34
+ @classmethod
35
+ def worst_outcome(cls, outcomes: Iterable['Outcome']) -> 'Outcome':
36
+ def _outcome_to_int(o: 'Outcome') -> int:
37
+ return cls._member_names_.index(o.name)
38
+
39
+ return max(outcomes, key=_outcome_to_int)
40
+
34
41
 
35
42
  class DigestHolder(BaseModel):
36
43
  value: Optional[str] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rbx.cp
3
- Version: 0.5.24
3
+ Version: 0.5.26
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9,<4.0
@@ -5,14 +5,14 @@ rbx/box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  rbx/box/builder.py,sha256=0TiRQJoHqLHAI8QwBrscbaJmhdcmialVtP_oEkfHcs0,3260
6
6
  rbx/box/cd.py,sha256=9a_SOnzoJBXxxffp4Wbf3UKXIwKuN3Hvj7K6SocALwE,1194
7
7
  rbx/box/checkers.py,sha256=VpgDzevOK7hrffG2zJGxquNiu-a9Fl3wquLn7xadcK0,6285
8
- rbx/box/code.py,sha256=9VKwvIyQRaoINg1lRzDgJlibDNLdzf5CCPYxsAtYEcU,11528
8
+ rbx/box/code.py,sha256=N2pjerRsESDqQ0G8NNeUJQgXxlJTZwNmanhhXZn5tN8,11531
9
9
  rbx/box/compile.py,sha256=OJLthDQ921w9vyoE6Gk1Df54i5RwtRJ2YG-8XEfefcs,2489
10
10
  rbx/box/conftest.py,sha256=sEmciXSeDC-wmrZ1JSxbsUenKNP_VWW32mrCun2pY3I,1070
11
11
  rbx/box/contest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  rbx/box/contest/build_contest_statements.py,sha256=OvKTE2O1UchiBRPzbU94KdIn0whT00VUbyPzUDlJRJg,11290
13
13
  rbx/box/contest/contest_package.py,sha256=OaUbpBtkhkgOPzJ1ccI_Vq4FMSaJvZm3gMOKfVY8oy4,3032
14
14
  rbx/box/contest/contest_utils.py,sha256=TDE7I6YQJlu4dQd68wzOp019bNgqiT0RlM-LMQMjL9w,301
15
- rbx/box/contest/main.py,sha256=S2UuDKOAgXfPLW_5MkkxpSADx0yMofpK1ud_Sw2foks,7328
15
+ rbx/box/contest/main.py,sha256=fFYeZn8KTLt9j-OFFkcBeQWw8RsTLUauFV6drM2hT48,7404
16
16
  rbx/box/contest/schema.py,sha256=rxzjJasMWPKKhvSJs4eW4A2oCiA4gXgfF-MzqsbPslQ,4914
17
17
  rbx/box/contest/statements.py,sha256=Pe4uo1hxvEON8O11VAzsOP3DxUel0vmwiAmolh4ltEs,2910
18
18
  rbx/box/creation.py,sha256=mVHVVj8ozX9D5qpkLewE5WSjF6HtTm74Pkwubk-bATg,2259
@@ -22,7 +22,7 @@ rbx/box/environment.py,sha256=47NtyuVC6zSQKAtQaXPEXvqcD-KJiuWRpWF8pYvcG4c,11158
22
22
  rbx/box/extensions.py,sha256=gIC73VbF1897er3iIMhaIw6GE8o1t43M7q97Iz7-_lg,503
23
23
  rbx/box/generators.py,sha256=bzURQrNEscCO8_3BYXCyGK9ZneX4-eOJZP5JJV28f3I,16350
24
24
  rbx/box/generators_test.py,sha256=mQqHepAMYa6zV_PseQALI0nIX6AdQktt6lh94muFhNw,1758
25
- rbx/box/main.py,sha256=pc7DiIaPnq50DUPsPhtzr4kiPu5c2TrK0QaS21wcmwY,22067
25
+ rbx/box/main.py,sha256=PZYbC2k6f2pMIuj1-X_yEANCWYstkIXJ7Bg0Szi37YA,23293
26
26
  rbx/box/package.py,sha256=SSckCXo7Zh412_qjYhSNwConjhR0Sk8911qGJU34Hac,11851
27
27
  rbx/box/packaging/boca/extension.py,sha256=hQhcbocNfW2ESv5RalS1wf6uvOoOfOnR_gHvbXUbSzY,852
28
28
  rbx/box/packaging/boca/packager.py,sha256=FOhSRg5K5Y4qNB0WyTR3DKgrpObf9I0JbyGpJHOtxpo,10673
@@ -32,14 +32,14 @@ rbx/box/packaging/packager.py,sha256=suCT_SLnWa915rV2j8VFqzH43HGKRTr9mGGlrvj45aw
32
32
  rbx/box/packaging/polygon/packager.py,sha256=HNpxP2nclLChSnrQtkT7tLwDdXHl1dzXMIF5RZwr9M4,10811
33
33
  rbx/box/packaging/polygon/test.py,sha256=bgEju5PwudgyfwxXJagm8fM6CJVlWM6l_-2q1V-oKaQ,3069
34
34
  rbx/box/packaging/polygon/xml_schema.py,sha256=-r24bCeRMGLrGGoT9FIgmqr87xHL-JzrFaR6bztbYtw,2703
35
- rbx/box/presets/__init__.py,sha256=Wiegp1onXPaZs8RE1J3PKT5j3PFWKw2U2rkgOSbnYeM,17529
35
+ rbx/box/presets/__init__.py,sha256=jivkMH5lWuNpwg4HExpacgPo7RqaENqmrE86aq3CJ6A,18643
36
36
  rbx/box/presets/fetch.py,sha256=F-BCOlvEBEyDqtOhiDuGPn4EDtA4Bwm-fqHJ7zZGlW8,1975
37
37
  rbx/box/presets/lock_schema.py,sha256=6sRPnyePOC8yy-5WcD5JRZdDJHf8loqbvpQ1IPiOU9s,349
38
38
  rbx/box/presets/schema.py,sha256=mZmSPkQsw7eQM0lQN6er1MO_LiW1ObwwAZFDK0F5fxE,1962
39
39
  rbx/box/sanitizers/warning_stack.py,sha256=RI97_GJgdjTKIXY_r0EKp5h0qQQSDSdNDh5K7zINrqs,2861
40
40
  rbx/box/schema.py,sha256=mPEOchzoGDwk_S9wUw1DKqwJWJ0S5GTxQnZIlm9BFwo,13709
41
41
  rbx/box/setter_config.py,sha256=6nGTPMvnJ7y1sM-EBuI493NSZOIiOZ1DTypSXrL-HRY,3686
42
- rbx/box/solutions.py,sha256=0kVFY_RdQQ-wK9hk4XY4VeJv5oPNkkq28x1sk8RNP3o,36584
42
+ rbx/box/solutions.py,sha256=GmjWlFDHZSHk44zGnISMEua1z3PE7XqjjdqHRaPSDG8,38041
43
43
  rbx/box/solutions_test.py,sha256=Cx7Goon_0sz_PaUcD8qa8gmjgzOVub6VHss3CB0GaA0,1524
44
44
  rbx/box/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  rbx/box/statements/build_statements.py,sha256=upsMT-cAnSvbmKgtijdFc0OxPcyeBxRG92hY6dN-ZOk,11920
@@ -58,13 +58,13 @@ rbx/box/ui/captured_log.py,sha256=ptICDPViVnz-_2NfrcB0SSBXNW5L74zI-vAZNN7kSok,11
58
58
  rbx/box/ui/css/app.tcss,sha256=apd5PkPEvl5jK3kE2qrxPyVED1VnvSsj08QQwzUPwEA,786
59
59
  rbx/box/ui/main.py,sha256=b0rHcBF42W4AOCv7WhtiGf_rUnY0yxpqO5oj3wfR4R4,984
60
60
  rbx/box/ui/run.py,sha256=wMEXrEFdQvMHz2hRKAFIithTnTtaL0kNQZu0jKmb8jI,7060
61
- rbx/box/validators.py,sha256=Vro6PytOsaenEnufR40NcY2k5OHyyrEDEcMT3gypBgw,8388
61
+ rbx/box/validators.py,sha256=SNOzOk2lJdxnU3e6TFPaqaBwz-qisYLR44vbiH1F4Hc,8806
62
62
  rbx/box/validators_test.py,sha256=hriR6rD32Ouu64eKYYTPLZVvqMxXj7Q2h1l_JAefL7U,344
63
63
  rbx/checker.py,sha256=pj1jO3my48ru-qugbER5onccANCjoR0-PaFe3H3VGEY,4118
64
64
  rbx/clone.py,sha256=wpHyED0_7ST7LD3vj7HjXhzqEzlwh6dRQvKQVDYhGeU,6744
65
65
  rbx/config.py,sha256=gM0-3ORnCoXNpIxIA3EAXEaiNY7s_NPzD7WbFYGbrlA,7436
66
66
  rbx/conftest.py,sha256=ouilbOIpvX8jTEdCAiWT85CbdBQKUUf41BjmDI82u-Y,967
67
- rbx/console.py,sha256=l0iulQH3_jQEm455W66TbDtC4a8owkWTHIIQpJaXofQ,715
67
+ rbx/console.py,sha256=X8EJy68OROgh6ao3ZcUjZm5Y56VFMzen58ywAuQ7pAU,990
68
68
  rbx/create.py,sha256=ezUq9KiSA-88ASd8CtjWXw8UB4LCaQ3Gib3OgvsLK-Q,986
69
69
  rbx/edit.py,sha256=Zqnx_Pt06ijCxV-pZKGCJhjKB-nVO0QCM6xSBwPWGoE,798
70
70
  rbx/grading/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -81,7 +81,7 @@ rbx/grading/judge/sandboxes/timeit.py,sha256=xScfasI2lsSQGZVpIZ7qBZfi0IaKC-1k8wO
81
81
  rbx/grading/judge/storage.py,sha256=FirqjwDqb0m0h2OTFyWrZL7CQ4XjZNxhqB4JpnDIhZY,9485
82
82
  rbx/grading/judge/test.py,sha256=ll0Iw7zyOpGdKPD_PGH7dvUkb4stQLu-ikbQnqJvuAc,944
83
83
  rbx/grading/judge/testiso.py,sha256=v14DtkWiZFJ9AKMzrb0_vZKPWDt8jz8iIw1Z2O-Advk,1397
84
- rbx/grading/steps.py,sha256=S1ozeLrteAMEHiNKXYxHUK90VQQoFz-lIcIkh6RU6uU,22784
84
+ rbx/grading/steps.py,sha256=EKH9k5buU69f8wgek6rAk72oAnDJ22nOp2Rw-touk_Q,23037
85
85
  rbx/grading/steps_with_caching.py,sha256=5cI71VSjEaDzCPQikpamG_EjZqkkFKKdBtyJeA4QB7Q,1531
86
86
  rbx/grading/steps_with_caching_run_test.py,sha256=nRzB4OcXkb-kQ4WCj0iTGVfBACllxZ0Ek5RSwfoJRgo,15262
87
87
  rbx/grading_utils.py,sha256=lL2KtSkOsMElqrRoApQTbFcqVOeHVWUDTMCa3IsLpC4,4484
@@ -162,8 +162,8 @@ rbx/testdata/caching/executable.py,sha256=WKRHNf_fprFJd1Fq1ubmQtR3mZzTYVNwKPLWuZ
162
162
  rbx/testdata/compatible,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
163
  rbx/testing_utils.py,sha256=ZZLKMUHlZ4HwsuNY50jqSBJ9HhpnFdba7opjDsvXE1U,2084
164
164
  rbx/utils.py,sha256=WlmnF4whc0-6ksVZoOhmom2bR2spT6zETFHjnpJOCsA,4383
165
- rbx_cp-0.5.24.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
166
- rbx_cp-0.5.24.dist-info/METADATA,sha256=oIiRQJKg7rlcz6fJfap3_0yZrNT2uWDW1TH8VG33NCY,3290
167
- rbx_cp-0.5.24.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
168
- rbx_cp-0.5.24.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
169
- rbx_cp-0.5.24.dist-info/RECORD,,
165
+ rbx_cp-0.5.26.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
166
+ rbx_cp-0.5.26.dist-info/METADATA,sha256=89qCk1_R5uGy7JXZieXOmYMBb4PY1YI4L94gxlsRq40,3290
167
+ rbx_cp-0.5.26.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
168
+ rbx_cp-0.5.26.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
169
+ rbx_cp-0.5.26.dist-info/RECORD,,