rbx.cp 0.13.3__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/annotations.py +5 -5
- rbx/box/checkers.py +26 -22
- rbx/box/cli.py +0 -4
- rbx/box/code.py +27 -80
- rbx/box/contest/build_contest_statements.py +16 -3
- rbx/box/contest/schema.py +1 -2
- rbx/box/environment.py +16 -6
- rbx/box/fields.py +25 -1
- rbx/box/generators.py +31 -5
- rbx/box/global_package.py +6 -2
- rbx/box/header.py +31 -11
- rbx/box/package.py +3 -15
- rbx/box/presets/__init__.py +2 -2
- rbx/box/schema.py +4 -25
- rbx/box/setter_config.py +11 -0
- rbx/box/solutions.py +12 -4
- rbx/box/statements/build_statements.py +5 -1
- rbx/box/statements/builders.py +7 -7
- rbx/box/statements/schema.py +11 -2
- rbx/box/tasks.py +9 -4
- rbx/box/testcase_utils.py +2 -0
- rbx/box/testing/__init__.py +0 -0
- rbx/box/testing/testing_package.py +246 -0
- rbx/box/testing/testing_preset.py +36 -0
- rbx/box/testing/testing_shared.py +81 -0
- rbx/box/ui/screens/run_explorer.py +0 -8
- rbx/box/ui/utils/run_ui.py +7 -3
- rbx/box/ui/widgets/test_output_box.py +1 -1
- rbx/box/validators.py +5 -2
- rbx/grading/caching.py +67 -16
- rbx/grading/judge/program.py +268 -0
- rbx/grading/judge/sandbox.py +30 -193
- rbx/grading/judge/sandboxes/stupid_sandbox.py +232 -241
- rbx/grading/judge/sandboxes/tee.py +31 -0
- rbx/grading/steps.py +87 -199
- rbx/grading/steps_with_caching.py +15 -6
- rbx/resources/presets/default/problem/problem.rbx.yml +0 -2
- rbx/resources/presets/default/shared/contest_template.rbx.tex +1 -1
- rbx/resources/presets/default/shared/problem_template.rbx.tex +5 -1
- rbx/resources/templates/rbx.h +43 -2
- rbx/testing_utils.py +8 -1
- rbx/utils.py +59 -1
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/METADATA +2 -1
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/RECORD +47 -67
- rbx/box/conftest.py +0 -42
- rbx/box/generators_test.py +0 -67
- rbx/box/lazy_importing_test.py +0 -25
- rbx/box/solutions_test.py +0 -47
- rbx/box/validators_test.py +0 -15
- rbx/checker.py +0 -128
- rbx/clone.py +0 -197
- rbx/conftest.py +0 -38
- rbx/create.py +0 -37
- rbx/edit.py +0 -24
- rbx/grading/conftest.py +0 -33
- rbx/grading/judge/sandboxes/isolate.py +0 -695
- rbx/grading/judge/testiso.py +0 -54
- rbx/grading/steps_with_caching_run_test.py +0 -707
- rbx/grading_utils.py +0 -148
- rbx/hydration.py +0 -101
- rbx/main.py +0 -118
- rbx/metadata.py +0 -105
- rbx/resources/envs/isolate.rbx.yml +0 -36
- rbx/resources/presets/default/problem/sols/slow.cpp +0 -15
- rbx/run.py +0 -45
- rbx/schema.py +0 -64
- rbx/submit.py +0 -61
- rbx/test.py +0 -349
- rbx/testcase.py +0 -70
- rbx/testcase_rendering.py +0 -79
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/LICENSE +0 -0
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/WHEEL +0 -0
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/entry_points.txt +0 -0
rbx/grading/judge/sandbox.py
CHANGED
@@ -3,16 +3,16 @@ import asyncio
|
|
3
3
|
import collections
|
4
4
|
import dataclasses
|
5
5
|
import io
|
6
|
+
import json
|
6
7
|
import logging
|
7
8
|
import os
|
8
9
|
import pathlib
|
9
10
|
import select
|
10
|
-
import signal
|
11
11
|
import stat
|
12
12
|
import subprocess
|
13
13
|
import sys
|
14
14
|
import typing
|
15
|
-
from typing import IO, Any, Dict, List, Optional
|
15
|
+
from typing import IO, Any, Dict, List, Optional, Tuple
|
16
16
|
|
17
17
|
import pydantic
|
18
18
|
|
@@ -210,6 +210,22 @@ class SandboxParams(pydantic.BaseModel):
|
|
210
210
|
self.dirs.append(DirectoryMount(src, dest, options))
|
211
211
|
|
212
212
|
|
213
|
+
class SandboxLog(pydantic.BaseModel):
|
214
|
+
"""A log of the sandbox."""
|
215
|
+
|
216
|
+
params: SandboxParams
|
217
|
+
execution_time: float # seconds
|
218
|
+
memory_used: int # bytes
|
219
|
+
exitcode: int
|
220
|
+
exitstatus: str
|
221
|
+
killing_signal: Optional[int] = None
|
222
|
+
exit_index: int = 0
|
223
|
+
other_logs: Dict[str, Any] = pydantic.Field(default_factory=dict)
|
224
|
+
|
225
|
+
def dump_other_logs(self) -> str:
|
226
|
+
return json.dumps(self.other_logs)
|
227
|
+
|
228
|
+
|
213
229
|
class SandboxBase(abc.ABC):
|
214
230
|
"""A base class for all sandboxes, meant to contain common
|
215
231
|
resources.
|
@@ -229,16 +245,12 @@ class SandboxBase(abc.ABC):
|
|
229
245
|
file_cacher: cacher.FileCacher
|
230
246
|
name: str
|
231
247
|
temp_dir: Optional[pathlib.Path]
|
232
|
-
cmd_file: pathlib.Path
|
233
|
-
|
234
|
-
params: SandboxParams
|
235
248
|
|
236
249
|
def __init__(
|
237
250
|
self,
|
238
251
|
file_cacher: Optional[cacher.FileCacher] = None,
|
239
252
|
name: Optional[str] = None,
|
240
253
|
temp_dir: Optional[pathlib.Path] = None,
|
241
|
-
params: Optional[SandboxParams] = None,
|
242
254
|
):
|
243
255
|
"""Initialization.
|
244
256
|
|
@@ -253,56 +265,7 @@ class SandboxBase(abc.ABC):
|
|
253
265
|
self.file_cacher = file_cacher or cacher.FileCacher(storage.NullStorage())
|
254
266
|
self.name = name if name is not None else 'unnamed'
|
255
267
|
self.temp_dir = temp_dir
|
256
|
-
|
257
|
-
self.cmd_file = pathlib.PosixPath('commands.log')
|
258
|
-
|
259
|
-
self.params = params or SandboxParams()
|
260
|
-
self.pid = None
|
261
|
-
self.pid_event = Event_ts()
|
262
|
-
|
263
|
-
# Set common environment variables.
|
264
|
-
# Specifically needed by Python, that searches the home for
|
265
|
-
# packages.
|
266
|
-
self.params.set_env['HOME'] = './'
|
267
|
-
|
268
|
-
def set_params(self, params: SandboxParams):
|
269
|
-
"""Set the parameters of the sandbox.
|
270
|
-
|
271
|
-
params (SandboxParams): the parameters to set.
|
272
|
-
|
273
|
-
"""
|
274
|
-
self.params = params
|
275
|
-
|
276
|
-
def set_multiprocess(self, multiprocess: bool):
|
277
|
-
"""Set the sandbox to (dis-)allow multiple threads and processes.
|
278
|
-
|
279
|
-
multiprocess (bool): whether to allow multiple thread/processes or not.
|
280
|
-
|
281
|
-
"""
|
282
|
-
if multiprocess:
|
283
|
-
# Max processes is set to 1000 to limit the effect of fork bombs.
|
284
|
-
self.params.max_processes = 1000
|
285
|
-
else:
|
286
|
-
self.params.max_processes = 1
|
287
|
-
|
288
|
-
def get_stats(self) -> str:
|
289
|
-
"""Return a human-readable string representing execution time
|
290
|
-
and memory usage.
|
291
|
-
|
292
|
-
return (string): human-readable stats.
|
293
|
-
|
294
|
-
"""
|
295
|
-
execution_time = self.get_execution_time()
|
296
|
-
if execution_time is not None:
|
297
|
-
time_str = f'{execution_time:.3f} sec'
|
298
|
-
else:
|
299
|
-
time_str = '(time unknown)'
|
300
|
-
memory_used = self.get_memory_used()
|
301
|
-
if memory_used is not None:
|
302
|
-
mem_str = f'{memory_used / (1024 * 1024):.2f} MB'
|
303
|
-
else:
|
304
|
-
mem_str = '(memory usage unknown)'
|
305
|
-
return f'[{time_str} - {mem_str}]'
|
268
|
+
self.cmd_file = pathlib.PosixPath('__commands__.log')
|
306
269
|
|
307
270
|
@abc.abstractmethod
|
308
271
|
def get_root_path(self) -> pathlib.Path:
|
@@ -313,101 +276,6 @@ class SandboxBase(abc.ABC):
|
|
313
276
|
"""
|
314
277
|
pass
|
315
278
|
|
316
|
-
@abc.abstractmethod
|
317
|
-
def get_execution_time(self) -> Optional[float]:
|
318
|
-
"""Return the time spent in the sandbox.
|
319
|
-
|
320
|
-
return (float): time spent in the sandbox.
|
321
|
-
|
322
|
-
"""
|
323
|
-
pass
|
324
|
-
|
325
|
-
@abc.abstractmethod
|
326
|
-
def get_memory_used(self) -> Optional[int]:
|
327
|
-
"""Return the memory used by the sandbox.
|
328
|
-
|
329
|
-
return (int): memory used by the sandbox (in bytes).
|
330
|
-
|
331
|
-
"""
|
332
|
-
pass
|
333
|
-
|
334
|
-
@abc.abstractmethod
|
335
|
-
def get_killing_signal(self) -> int:
|
336
|
-
"""Return the signal that killed the sandboxed process.
|
337
|
-
|
338
|
-
return (int): offending signal, or 0.
|
339
|
-
|
340
|
-
"""
|
341
|
-
pass
|
342
|
-
|
343
|
-
@abc.abstractmethod
|
344
|
-
def get_exit_status(self) -> str:
|
345
|
-
"""Get information about how the sandbox terminated.
|
346
|
-
|
347
|
-
return (string): the main reason why the sandbox terminated.
|
348
|
-
|
349
|
-
"""
|
350
|
-
pass
|
351
|
-
|
352
|
-
@abc.abstractmethod
|
353
|
-
def get_exit_code(self) -> int:
|
354
|
-
"""Return the exit code of the sandboxed process.
|
355
|
-
|
356
|
-
return (int): exitcode, or 0.
|
357
|
-
|
358
|
-
"""
|
359
|
-
pass
|
360
|
-
|
361
|
-
def set_pid(self, pid: int):
|
362
|
-
"""Set the PID of the sandboxed process.
|
363
|
-
|
364
|
-
pid (int): the PID of the sandboxed process.
|
365
|
-
|
366
|
-
"""
|
367
|
-
self.pid = pid
|
368
|
-
self.pid_event.set()
|
369
|
-
|
370
|
-
async def get_pid(self) -> int:
|
371
|
-
"""Return the PID of the sandboxed process.
|
372
|
-
|
373
|
-
Blocks until the PID is set.
|
374
|
-
|
375
|
-
return (int): the PID of the sandboxed process.
|
376
|
-
|
377
|
-
"""
|
378
|
-
await self.pid_event.wait()
|
379
|
-
assert self.pid is not None
|
380
|
-
return self.pid
|
381
|
-
|
382
|
-
def clear_pid(self):
|
383
|
-
"""Clear the PID of the sandboxed process."""
|
384
|
-
self.pid_event.clear()
|
385
|
-
self.pid = None
|
386
|
-
|
387
|
-
def use_pgid(self) -> bool:
|
388
|
-
"""Whether the sandbox supports process groups."""
|
389
|
-
return False
|
390
|
-
|
391
|
-
@abc.abstractmethod
|
392
|
-
def get_detailed_logs(self) -> str:
|
393
|
-
"""Return the detailed logs of the sandbox.
|
394
|
-
|
395
|
-
return (string): the detailed logs of the sandbox.
|
396
|
-
|
397
|
-
"""
|
398
|
-
pass
|
399
|
-
|
400
|
-
@abc.abstractmethod
|
401
|
-
def get_human_exit_description(self) -> str:
|
402
|
-
"""Get the status of the sandbox and return a human-readable
|
403
|
-
string describing it.
|
404
|
-
|
405
|
-
return (string): human-readable explaination of why the
|
406
|
-
sandbox terminated.
|
407
|
-
|
408
|
-
"""
|
409
|
-
pass
|
410
|
-
|
411
279
|
def use_soft_timeout(self) -> bool:
|
412
280
|
return False
|
413
281
|
|
@@ -702,52 +570,24 @@ class SandboxBase(abc.ABC):
|
|
702
570
|
]
|
703
571
|
|
704
572
|
@abc.abstractmethod
|
705
|
-
def
|
573
|
+
def run(
|
706
574
|
self,
|
707
575
|
command: List[str],
|
708
|
-
|
709
|
-
|
710
|
-
subprocess.Popen and discarding standard input, output and
|
711
|
-
error. More specifically, the standard input gets closed just
|
712
|
-
after the execution has started; standard output and error are
|
713
|
-
read until the end, in a way that prevents the execution from
|
714
|
-
being blocked because of insufficient buffering.
|
715
|
-
|
716
|
-
command ([string]): executable filename and arguments of the
|
717
|
-
command.
|
718
|
-
wait (bool): True if this call is blocking, False otherwise
|
719
|
-
|
720
|
-
return (bool|Popen): if the call is blocking, then return True
|
721
|
-
if the sandbox didn't report errors (caused by the sandbox
|
722
|
-
itself), False otherwise; if the call is not blocking,
|
723
|
-
return the Popen object from subprocess.
|
724
|
-
|
725
|
-
"""
|
576
|
+
params: SandboxParams,
|
577
|
+
) -> SandboxLog:
|
726
578
|
pass
|
727
579
|
|
728
580
|
@abc.abstractmethod
|
729
|
-
def
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
581
|
+
def run_communication(
|
582
|
+
self,
|
583
|
+
command: List[str],
|
584
|
+
params: SandboxParams,
|
585
|
+
interactor_command: List[str],
|
586
|
+
interactor_params: SandboxParams,
|
587
|
+
merged_capture: Optional[pathlib.Path] = None,
|
588
|
+
) -> Tuple[SandboxLog, SandboxLog]:
|
735
589
|
pass
|
736
590
|
|
737
|
-
def translate_box_exitcode(self, exitcode: int) -> bool:
|
738
|
-
"""Translate the sandbox exit code to a boolean sandbox success.
|
739
|
-
|
740
|
-
_ (int): the exit code of the sandbox.
|
741
|
-
|
742
|
-
return (bool): False if the sandbox had an error, True if it
|
743
|
-
terminated correctly (regardless of what the internal process
|
744
|
-
did).
|
745
|
-
|
746
|
-
"""
|
747
|
-
# SIGTERM can be safely ignored, just in case it leaks away from
|
748
|
-
# the sandbox.
|
749
|
-
return exitcode == 0 or exitcode == -signal.SIGTERM
|
750
|
-
|
751
591
|
@abc.abstractmethod
|
752
592
|
def initialize(self):
|
753
593
|
"""Initialize the sandbox.
|
@@ -779,9 +619,6 @@ class SandboxBase(abc.ABC):
|
|
779
619
|
"""
|
780
620
|
pass
|
781
621
|
|
782
|
-
def debug_message(self) -> Any:
|
783
|
-
return 'N/A'
|
784
|
-
|
785
622
|
|
786
623
|
class Truncator(io.RawIOBase):
|
787
624
|
"""Wrap a file-like object to simulate truncation.
|