rbx.cp 0.13.4__py3-none-any.whl → 0.13.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rbx/box/checkers.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import pathlib
2
- import signal
3
2
  from typing import List, Optional
4
3
 
5
4
  import typer
@@ -248,6 +247,7 @@ async def _check(
248
247
  console.console.print(
249
248
  f'[error]Summary:[/error] {checker_run_log.get_summary()}'
250
249
  )
250
+ console.console.print(f'[error]Message:[/error] {message}')
251
251
  console.console.print(
252
252
  f'[error]Testcase input:[/error] [item]{testcase.inputPath}[/item]'
253
253
  )
@@ -345,14 +345,7 @@ async def check_communication(
345
345
  if (
346
346
  interactor_run_log is not None
347
347
  and run_log is not None
348
- and (
349
- run_log.exitcode == -signal.SIGPIPE
350
- or run_log.exitstatus == SandboxBase.EXIT_TERMINATED
351
- or (
352
- run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
353
- and not _is_testlib_eof(interactor_stderr.read_text())
354
- )
355
- )
348
+ and (interactor_run_log.exitindex < run_log.exitindex)
356
349
  ):
357
350
  result = _check_interactor()
358
351
  if result is not None and result.outcome != Outcome.ACCEPTED:
rbx/box/cli.py CHANGED
@@ -286,7 +286,6 @@ async def run(
286
286
  help='Whether to pick solutions interactively.',
287
287
  ),
288
288
  ):
289
- console.console.log(check)
290
289
  main_solution = package.get_main_solution()
291
290
  if check and main_solution is None:
292
291
  console.console.print(
rbx/box/code.py CHANGED
@@ -12,7 +12,7 @@ import rich.text
12
12
  import typer
13
13
  from pydantic import BaseModel
14
14
 
15
- from rbx import console, utils
15
+ from rbx import console
16
16
  from rbx.box import download, global_package, package, setter_config, state
17
17
  from rbx.box.environment import (
18
18
  CompilationConfig,
@@ -48,6 +48,8 @@ from rbx.grading.steps import (
48
48
  maybe_get_bits_stdcpp_for_commands,
49
49
  )
50
50
 
51
+ MERGED_CAPTURE_FILENAME = 'merged_capture.pio'
52
+
51
53
 
52
54
  class SanitizationLevel(Enum):
53
55
  NONE = 0
@@ -207,6 +209,9 @@ def _format_stack_limit(limit: int) -> str:
207
209
 
208
210
 
209
211
  def _check_stack_limit():
212
+ cfg = setter_config.get_setter_config()
213
+ if not cfg.judging.check_stack:
214
+ return
210
215
  if not state.STATE.run_through_cli:
211
216
  return
212
217
  soft, hard = resource.RLIM_INFINITY, resource.RLIM_INFINITY
@@ -261,52 +266,6 @@ class PreparedRun:
261
266
  metadata: RunLogMetadata
262
267
 
263
268
 
264
- @dataclasses.dataclass
265
- class CaptureSpec:
266
- prefix: str
267
- output: Optional[DigestOrDest] = None
268
- merged_capture: Optional[pathlib.Path] = None
269
-
270
-
271
- def _prepare_for_communication(
272
- run: PreparedRun,
273
- stdin: pathlib.Path,
274
- stdout: pathlib.Path,
275
- reverse_io: bool = False,
276
- capture: Optional[CaptureSpec] = None,
277
- ):
278
- run.sandbox_params.set_stdio(
279
- stdin=stdin,
280
- stdout=stdout,
281
- )
282
- run.sandbox_params.reverse_io = reverse_io
283
- if capture is not None:
284
- run.sandbox_params.timeit_prefix = capture.prefix
285
-
286
- if capture.output is not None:
287
- output_path = PosixPath('capture')
288
- run.sandbox_params.timeit_dups['do'].append(output_path)
289
-
290
- run.artifacts.outputs.append(
291
- GradingFileOutput(
292
- src=output_path,
293
- **capture.output.expand(),
294
- touch=True,
295
- )
296
- )
297
-
298
- if capture.merged_capture is not None:
299
- merged_output_path = utils.abspath(package.get_merged_capture_path())
300
- run.sandbox_params.timeit_dups['Do'].append(merged_output_path)
301
-
302
- run.artifacts.outputs.append(
303
- GradingFileOutput(
304
- src=merged_output_path,
305
- dest=capture.merged_capture,
306
- )
307
- )
308
-
309
-
310
269
  def _prepare_run(
311
270
  code: CodeItem,
312
271
  executable: DigestOrSource,
@@ -318,12 +277,13 @@ def _prepare_run(
318
277
  extra_args: Optional[str] = None,
319
278
  extra_config: Optional[ExecutionConfig] = None,
320
279
  retry_index: Optional[int] = None,
280
+ file_prefix: Optional[str] = None,
321
281
  ):
322
282
  language = find_language_name(code)
323
283
  execution_options = get_execution_config(language)
324
284
  if extra_config is not None:
325
285
  execution_options = merge_execution_configs([execution_options, extra_config])
326
- file_mapping = get_file_mapping(language)
286
+ file_mapping = get_file_mapping(language, file_prefix)
327
287
  sandbox_params = get_sandbox_params_from_config(execution_options.sandbox)
328
288
 
329
289
  # Sanitization parameters.
@@ -739,6 +699,7 @@ async def run_item(
739
699
  class CommunicationItem:
740
700
  code: CodeItem
741
701
  executable: DigestOrSource
702
+ file_prefix: str
742
703
  stderr: Optional[DigestOrDest] = None
743
704
  inputs: Optional[List[GradingFileInput]] = None
744
705
  outputs: Optional[List[GradingFileOutput]] = None
@@ -750,21 +711,22 @@ class CommunicationItem:
750
711
  return _prepare_run(
751
712
  self.code,
752
713
  self.executable,
714
+ stdout=self.capture,
753
715
  stderr=self.stderr,
754
716
  inputs=self.inputs,
755
717
  outputs=self.outputs,
756
718
  extra_args=self.extra_args,
757
719
  extra_config=self.extra_config,
720
+ file_prefix=self.file_prefix,
758
721
  )
759
722
 
760
723
 
761
724
  async def run_communication(
762
725
  interactor: CommunicationItem,
763
726
  solution: CommunicationItem,
764
- merged_capture: Optional[pathlib.Path] = None,
727
+ merged_capture: Optional[DigestOrDest] = None,
765
728
  retry_index: Optional[int] = None,
766
729
  ):
767
- fifo_in, fifo_out = package.get_fifos()
768
730
  interactor_prepared = interactor.prepare()
769
731
  solution_prepared = solution.prepare()
770
732
 
@@ -772,48 +734,30 @@ async def run_communication(
772
734
  interactor_prepared.metadata.retryIndex = retry_index
773
735
  solution_prepared.metadata.retryIndex = retry_index
774
736
 
775
- interactor_prefix = '<'
776
- solution_prefix = '>'
737
+ grading_artifacts = GradingArtifacts()
738
+ grading_artifacts.inputs.extend(interactor_prepared.artifacts.inputs)
739
+ grading_artifacts.outputs.extend(interactor_prepared.artifacts.outputs)
740
+ grading_artifacts.inputs.extend(solution_prepared.artifacts.inputs)
741
+ grading_artifacts.outputs.extend(solution_prepared.artifacts.outputs)
777
742
 
743
+ merged_capture_path: Optional[pathlib.Path] = None
778
744
  if merged_capture is not None:
779
- package.get_merged_capture_path().write_text(
780
- f'{interactor_prefix}\n{solution_prefix}\n'
745
+ merged_capture_path = pathlib.Path(MERGED_CAPTURE_FILENAME)
746
+ grading_artifacts.outputs.append(
747
+ GradingFileOutput(
748
+ src=merged_capture_path,
749
+ **merged_capture.expand(),
750
+ )
781
751
  )
782
752
 
783
- _prepare_for_communication(
784
- interactor_prepared,
785
- fifo_out,
786
- fifo_in,
787
- capture=CaptureSpec(
788
- prefix=interactor_prefix,
789
- output=interactor.capture,
790
- merged_capture=merged_capture,
791
- ),
792
- )
793
- _prepare_for_communication(
794
- solution_prepared,
795
- fifo_in,
796
- fifo_out,
797
- reverse_io=True,
798
- capture=CaptureSpec(
799
- prefix=solution_prefix,
800
- output=solution.capture,
801
- merged_capture=merged_capture,
802
- ),
803
- )
804
-
805
753
  interactor_run_params = steps.CoordinatedRunParams(
806
754
  command=interactor_prepared.command,
807
755
  params=interactor_prepared.sandbox_params,
808
- sandbox=package.get_singleton_interactor_sandbox(),
809
- artifacts=interactor_prepared.artifacts,
810
756
  metadata=interactor_prepared.metadata,
811
757
  )
812
758
  solution_run_params = steps.CoordinatedRunParams(
813
759
  command=solution_prepared.command,
814
760
  params=solution_prepared.sandbox_params,
815
- sandbox=package.get_singleton_sandbox(),
816
- artifacts=solution_prepared.artifacts,
817
761
  metadata=solution_prepared.metadata,
818
762
  )
819
763
 
@@ -825,5 +769,8 @@ async def run_communication(
825
769
  return await steps_with_caching.run_coordinated(
826
770
  interactor_run_params,
827
771
  solution_run_params,
772
+ sandbox=package.get_singleton_sandbox(),
773
+ artifacts=grading_artifacts,
828
774
  dependency_cache=package.get_dependency_cache(),
775
+ merged_capture=merged_capture_path,
829
776
  )
rbx/box/environment.py CHANGED
@@ -10,7 +10,6 @@ from rbx import config, console, utils
10
10
  from rbx.box import presets
11
11
  from rbx.box.extensions import Extensions, LanguageExtensions
12
12
  from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
13
- from rbx.grading.judge.sandboxes.isolate import IsolateSandbox
14
13
  from rbx.grading.judge.sandboxes.stupid_sandbox import StupidSandbox
15
14
  from rbx.grading.limits import Limits
16
15
 
@@ -58,6 +57,12 @@ relative to the sandbox root.""",
58
57
  relative to the sandbox root.""",
59
58
  )
60
59
 
60
+ capture: str = Field(
61
+ default='capture',
62
+ description="""Path where to output the capture file after running the program,
63
+ relative to the sandbox root.""",
64
+ )
65
+
61
66
  compilable: str = Field(
62
67
  default='compilable',
63
68
  description="""Path where to copy the compilable file to before compiling the program,
@@ -232,7 +237,7 @@ execution config can be individually overridden in the language configuration.""
232
237
 
233
238
  sandbox: str = Field(
234
239
  default='stupid',
235
- description="""Identifier of the sandbox used by this environment (e.g. "stupid", "isolate")""",
240
+ description="""Identifier of the sandbox used by this environment (e.g. "stupid")""",
236
241
  )
237
242
 
238
243
  timing: TimingConfig = Field(
@@ -381,13 +386,20 @@ def get_execution_config(language: str) -> ExecutionConfig:
381
386
 
382
387
 
383
388
  @functools.cache
384
- def get_file_mapping(language: str) -> FileMapping:
389
+ def get_file_mapping(language: str, file_prefix: Optional[str] = None) -> FileMapping:
385
390
  environment = get_environment()
386
- return _merge_shallow_models(
391
+ mapping = _merge_shallow_models(
387
392
  FileMapping,
388
393
  environment.defaultFileMapping or FileMapping(),
389
394
  get_language(language).fileMapping or FileMapping(),
390
395
  )
396
+ if file_prefix is not None:
397
+ mapping.input = f'{file_prefix}_{mapping.input}'
398
+ mapping.output = f'{file_prefix}_{mapping.output}'
399
+ mapping.error = f'{file_prefix}_{mapping.error}'
400
+ mapping.compilable = f'{file_prefix}_{mapping.compilable}'
401
+ mapping.executable = f'{file_prefix}_{mapping.executable}'
402
+ return mapping
391
403
 
392
404
 
393
405
  @functools.cache
@@ -395,8 +407,6 @@ def get_sandbox_type() -> Type[SandboxBase]:
395
407
  used_sandbox = get_environment().sandbox
396
408
  if used_sandbox == 'stupid':
397
409
  return StupidSandbox
398
- if used_sandbox == 'isolate':
399
- return IsolateSandbox
400
410
  return StupidSandbox
401
411
 
402
412
 
rbx/box/generators.py CHANGED
@@ -120,15 +120,25 @@ def _copy_testcase_output_over(
120
120
 
121
121
 
122
122
  def _copy_testcase_outputs_over(
123
- testcase: Testcase, dest: Testcase, pipes: bool = False, dry_run: bool = False
123
+ testcase: Testcase,
124
+ dest: Testcase,
125
+ pipes: bool = False,
126
+ only_pipes: bool = False,
127
+ dry_run: bool = False,
124
128
  ):
129
+ if only_pipes:
130
+ pipes = True
125
131
  assert dest.outputPath is not None
126
132
  if not dry_run:
127
133
  dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
128
134
 
129
135
  has_copied = False
130
136
 
131
- if testcase.outputPath is not None and testcase.outputPath.is_file():
137
+ if (
138
+ not only_pipes
139
+ and testcase.outputPath is not None
140
+ and testcase.outputPath.is_file()
141
+ ):
132
142
  if not dry_run:
133
143
  _check_crlf(testcase.outputPath)
134
144
  shutil.copy(str(testcase.outputPath), str(dest.outputPath))
@@ -371,6 +381,7 @@ async def generate_output_for_testcase(
371
381
  main_solution_digest: str,
372
382
  testcase: Testcase,
373
383
  interactor_digest: Optional[str] = None,
384
+ capture_pipes: Optional[bool] = None,
374
385
  ):
375
386
  assert testcase.outputPath is not None
376
387
  testcase.inputPath.parent.mkdir(parents=True, exist_ok=True)
@@ -388,7 +399,7 @@ async def generate_output_for_testcase(
388
399
  interactor_digest=interactor_digest,
389
400
  use_retries=False,
390
401
  use_timelimit=False,
391
- capture_pipes=True,
402
+ capture_pipes=capture_pipes,
392
403
  )
393
404
 
394
405
  if eval.result.outcome.is_slow() and eval.result.no_tle_outcome == Outcome.ACCEPTED:
@@ -474,10 +485,22 @@ async def generate_outputs_for_testcases(
474
485
  raise typer.Exit(1)
475
486
 
476
487
  assert solution_digest is not None
488
+ capture_pipes = None
489
+ if (
490
+ pkg.type == TaskType.COMMUNICATION
491
+ and entry.metadata.copied_from is not None
492
+ ):
493
+ # If some pipe file is already specified, we don't need to capture the pipes
494
+ # when running the program.
495
+ capture_pipes = not _copy_testcase_outputs_over(
496
+ entry.metadata.copied_from, tc, only_pipes=True, dry_run=True
497
+ )
498
+
477
499
  await generate_output_for_testcase(
478
500
  solution_digest,
479
501
  tc,
480
502
  interactor_digest=interactor_digest,
503
+ capture_pipes=capture_pipes,
481
504
  )
482
505
  if entry.metadata.copied_from is not None:
483
506
  # Copy remaining pipe files.
rbx/box/global_package.py CHANGED
@@ -9,7 +9,7 @@ from rbx.grading.judge.sandbox import SandboxBase
9
9
  from rbx.grading.judge.sandboxes.stupid_sandbox import StupidSandbox
10
10
  from rbx.grading.judge.storage import FilesystemStorage, Storage
11
11
 
12
- CACHE_STEP_VERSION = 3
12
+ CACHE_STEP_VERSION = 4
13
13
 
14
14
 
15
15
  def get_cache_fingerprint() -> str:
rbx/box/header.py CHANGED
@@ -49,11 +49,23 @@ def _get_string_var_block() -> str:
49
49
  return _get_var_block(_get_vars_of_type(str, _string_repr))
50
50
 
51
51
 
52
+ def _check_int_bounds(x: int) -> None:
53
+ if x >= 2**64:
54
+ raise ValueError(
55
+ f'Some variable you defined (value: {x}) is too large to fit in a C++ 64-bit integer (signed or unsigned)'
56
+ )
57
+ if x < -(2**63):
58
+ raise ValueError(
59
+ f'Some variable you defined (value: {x}) is too small to fit in a C++ 64-bit signed integer (int64_t)'
60
+ )
61
+
62
+
52
63
  def _get_int_var_block() -> str:
53
64
  def _transform(x: Primitive) -> str:
54
65
  if isinstance(x, bool):
55
66
  return str(int(x))
56
- return str(x)
67
+ _check_int_bounds(int(x))
68
+ return f'static_cast<int64_t>({x})'
57
69
 
58
70
  return _get_var_block(_get_vars_of_type(int, _transform))
59
71
 
rbx/box/package.py CHANGED
@@ -1,8 +1,6 @@
1
1
  import atexit
2
2
  import functools
3
- import os
4
3
  import pathlib
5
- import shutil
6
4
  import sys
7
5
  from typing import Dict, List, Optional, Tuple
8
6
 
@@ -455,18 +453,6 @@ def get_empty_sentinel_path(root: pathlib.Path = pathlib.Path()) -> pathlib.Path
455
453
  return path
456
454
 
457
455
 
458
- @functools.cache
459
- def get_fifos(root: pathlib.Path = pathlib.Path()) -> Tuple[pathlib.Path, pathlib.Path]:
460
- path = get_shared_dir(root) / '.fifos'
461
- shutil.rmtree(path, ignore_errors=True)
462
- path.mkdir(parents=True, exist_ok=True)
463
- fifo_in = path / 'fifo.in'
464
- fifo_out = path / 'fifo.out'
465
- os.mkfifo(fifo_in)
466
- os.mkfifo(fifo_out)
467
- return fifo_in, fifo_out
468
-
469
-
470
456
  @functools.cache
471
457
  def get_merged_capture_path(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
472
458
  path = get_shared_dir(root) / '.merged_capture'
rbx/box/setter_config.py CHANGED
@@ -66,6 +66,13 @@ class CachingConfig(BaseModel):
66
66
  )
67
67
 
68
68
 
69
+ class JudgingConfig(BaseModel):
70
+ check_stack: bool = Field(
71
+ default=True,
72
+ description='Whether to check the stack size before running code.',
73
+ )
74
+
75
+
69
76
  class SetterConfig(BaseModel):
70
77
  sanitizers: SanitizersConfig = Field(
71
78
  default_factory=SanitizersConfig, # type: ignore
@@ -93,6 +100,10 @@ class SetterConfig(BaseModel):
93
100
  default_factory=CachingConfig, # type: ignore
94
101
  description='Configuration for caching.',
95
102
  )
103
+ judging: JudgingConfig = Field(
104
+ default_factory=JudgingConfig, # type: ignore
105
+ description='Configuration for judging.',
106
+ )
96
107
 
97
108
  def substitute_command(self, command: str, sanitized: bool = False) -> str:
98
109
  exe = shlex.split(command)[0]
rbx/box/solutions.py CHANGED
@@ -922,6 +922,12 @@ def get_worst_outcome(evals: List[Evaluation]) -> Outcome:
922
922
  return Outcome.worst_outcome(eval.result.outcome for eval in evals)
923
923
 
924
924
 
925
+ def get_truncated_message(message: str, max_length: int = 100) -> str:
926
+ if len(message) > max_length:
927
+ return message[:max_length] + '... (truncated)'
928
+ return message
929
+
930
+
925
931
  class SolutionOutcomeReport(BaseModel):
926
932
  solution: Solution
927
933
  evals: List[Evaluation]
@@ -971,9 +977,8 @@ class SolutionOutcomeReport(BaseModel):
971
977
  if print_message and self.message is not None:
972
978
  tc, msg = self.message
973
979
  if msg:
974
- if len(msg) > 100:
975
- msg = msg[:100] + '... (truncated)'
976
- res += f'\nMessage for {tc}: {msg}'
980
+ msg = get_truncated_message(msg)
981
+ res += f'\nMessage for {utils.escape_markup(str(tc))}: {utils.escape_markup(msg)}'
977
982
  return res
978
983
 
979
984
 
@@ -1471,7 +1476,10 @@ async def print_run_report(
1471
1476
  console.print(f' ({time}, {memory})', end='')
1472
1477
  checker_msg = eval.result.message
1473
1478
  if checker_msg:
1474
- console.print(f': [i]{checker_msg}[/i]', end='')
1479
+ checker_msg = get_truncated_message(checker_msg, 150)
1480
+ console.print(
1481
+ f': [i]{utils.escape_markup(checker_msg)}[/i]', end=''
1482
+ )
1475
1483
  else:
1476
1484
  console.print(f'{i}/', end='')
1477
1485
  console.print(get_testcase_markup_verdict(eval), end='')
rbx/box/tasks.py CHANGED
@@ -51,7 +51,7 @@ async def run_solution_on_testcase(
51
51
  timelimit_override: Optional[int] = None,
52
52
  use_retries: bool = True,
53
53
  use_timelimit: bool = True,
54
- capture_pipes: bool = False,
54
+ capture_pipes: Optional[bool] = None,
55
55
  nruns: int = 0,
56
56
  filestem: Optional[str] = None,
57
57
  is_stress: bool = False,
@@ -175,12 +175,13 @@ async def _run_communication_solution_on_testcase(
175
175
  timelimit_override: Optional[int] = None,
176
176
  use_retries: bool = True,
177
177
  use_timelimit: bool = True,
178
- capture_pipes: bool = False,
178
+ capture_pipes: Optional[bool] = None,
179
179
  nruns: int = 0,
180
180
  filestem: Optional[str] = None,
181
181
  is_stress: bool = False,
182
182
  ) -> Evaluation:
183
- capture_pipes = capture_pipes or state.STATE.debug_logs
183
+ if capture_pipes is None:
184
+ capture_pipes = state.STATE.debug_logs
184
185
 
185
186
  async def run_fn(retry_index: int) -> Evaluation:
186
187
  actual_sandbox = package.get_singleton_sandbox()
@@ -243,6 +244,7 @@ async def _run_communication_solution_on_testcase(
243
244
  capture=DigestOrDest.create(interactor_capture_path)
244
245
  if interactor_capture_path
245
246
  else None,
247
+ file_prefix='interactor',
246
248
  )
247
249
  solution_capture_path = (
248
250
  output_path.with_suffix('.pout') if capture_pipes else None
@@ -255,6 +257,7 @@ async def _run_communication_solution_on_testcase(
255
257
  capture=DigestOrDest.create(solution_capture_path)
256
258
  if solution_capture_path
257
259
  else None,
260
+ file_prefix='solution',
258
261
  )
259
262
 
260
263
  merged_capture_path = output_path.with_suffix('.pio') if capture_pipes else None
@@ -262,7 +265,9 @@ async def _run_communication_solution_on_testcase(
262
265
  interactor=interactor_item,
263
266
  solution=solution_item,
264
267
  retry_index=retry_index,
265
- merged_capture=merged_capture_path,
268
+ merged_capture=DigestOrDest.create(merged_capture_path)
269
+ if merged_capture_path
270
+ else None,
266
271
  )
267
272
 
268
273
  checker_result = await checkers.check_communication(
@@ -3,6 +3,7 @@ from dataclasses import dataclass
3
3
  from typing import Dict, List, Optional
4
4
 
5
5
  from rbx import console, utils
6
+ from rbx.box import presets
6
7
  from rbx.box.fields import Primitive
7
8
  from rbx.box.schema import (
8
9
  CodeItem,
@@ -52,8 +53,12 @@ class TestingPackage(TestingShared):
52
53
  )
53
54
 
54
55
  def initialize_preset(self) -> TestingPreset:
55
- preset_path = self.root / '.local.rbx'
56
- preset_path.mkdir(parents=True, exist_ok=True)
56
+ preset = presets.get_active_preset_or_null(self.root)
57
+ if preset is None:
58
+ preset_path = self.root / '.local.rbx'
59
+ preset_path.mkdir(parents=True, exist_ok=True)
60
+ else:
61
+ preset_path = presets.get_active_preset_path(self.root)
57
62
  return TestingPreset(preset_path)
58
63
 
59
64
  def print_tree(self):
@@ -6,8 +6,6 @@ from textual.reactive import reactive
6
6
  from textual.screen import Screen
7
7
  from textual.widgets import Footer, Header, Label, ListItem, ListView
8
8
 
9
- from rbx.box import package
10
- from rbx.box.schema import TaskType
11
9
  from rbx.box.solutions import SolutionReportSkeleton
12
10
  from rbx.box.ui.screens.error import ErrorScreen
13
11
  from rbx.box.ui.screens.run_test_explorer import RunTestExplorerScreen
@@ -42,12 +40,6 @@ class RunExplorerScreen(Screen):
42
40
  tips.markup = True
43
41
  tips.display = False
44
42
  tips.border_title = 'Tips'
45
- pkg = package.find_problem_package_or_die()
46
- if pkg.type == TaskType.COMMUNICATION:
47
- tips.display = True
48
- tips.write(
49
- 'This is an interactive problem.\nYou can use the [bold blue]rbx --capture run[/bold blue] command to capture the interaction between the processes and see them here.'
50
- )
51
43
  yield tips
52
44
 
53
45
  def on_mount(self):
@@ -92,7 +92,7 @@ def get_run_testcase_metadata_markup(
92
92
  )
93
93
  lines.append(f'[b]Time:[/b] {time_str} / [b]Memory:[/b] {memory_str}')
94
94
  if checker_msg is not None:
95
- lines.append(f'[b]Checker:[/b] {checker_msg}')
95
+ lines.append(f'[b]Checker:[/b] {utils.escape_markup(checker_msg)}')
96
96
  return '\n'.join(lines)
97
97
 
98
98
 
@@ -102,7 +102,11 @@ def get_metadata_markup(entry: GenerationTestcaseEntry) -> str:
102
102
  if entry.metadata.copied_from is not None:
103
103
  lines.append(f'[b]Copied from:[/b] {entry.metadata.copied_from.inputPath}')
104
104
  if entry.metadata.generator_call is not None:
105
- lines.append(f'[b]Gen. call:[/b] {entry.metadata.generator_call}')
105
+ lines.append(
106
+ f'[b]Gen. call:[/b] {utils.escape_markup(str(entry.metadata.generator_call))}'
107
+ )
106
108
  if entry.metadata.generator_script is not None:
107
- lines.append(f'[b]Gen. script:[/b] {entry.metadata.generator_script}')
109
+ lines.append(
110
+ f'[b]Gen. script:[/b] {utils.escape_markup(str(entry.metadata.generator_script))}'
111
+ )
108
112
  return '\n'.join(lines)
@@ -44,7 +44,7 @@ class TestBoxWidget(Widget, can_focus=False):
44
44
  output: FileLog
45
45
  stderr: FileLog
46
46
  log: FileLog
47
- interaction: FileLog
47
+ interaction: InteractionBox
48
48
 
49
49
  def logs(self) -> Logs:
50
50
  return self.Logs(
rbx/box/validators.py CHANGED
@@ -119,6 +119,7 @@ async def _validate_testcase(
119
119
  extra_args=shlex.join(var_args) if var_args else None,
120
120
  )
121
121
 
122
+ message = package.get_digest_as_string(message_digest.value or '')
122
123
  if (
123
124
  run_log is not None
124
125
  and run_log.exitcode != 0
@@ -128,12 +129,13 @@ async def _validate_testcase(
128
129
  f'[error]Validator [item]{validator.path}[/item] failed unexpectedly.[/error]'
129
130
  )
130
131
  console.console.print(f'[error]Summary:[/error] {run_log.get_summary()}')
132
+ console.console.print(f'[error]Message:[/error] {message}')
133
+ console.console.print(f'[error]Testcase:[/error] {testcase}')
131
134
  raise typer.Exit(1)
132
135
 
133
136
  log_overview = ''
134
137
  if log_digest.value is not None:
135
138
  log_overview = package.get_digest_as_string(log_digest.value or '')
136
- message = package.get_digest_as_string(message_digest.value or '')
137
139
  return (
138
140
  run_log is not None and run_log.exitcode == 0,
139
141
  message,