rbx.cp 0.5.38__tar.gz → 0.5.40__tar.gz
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_cp-0.5.38 → rbx_cp-0.5.40}/PKG-INFO +1 -1
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/pyproject.toml +1 -1
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/checkers.py +5 -1
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/code.py +1 -1
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/main.py +100 -19
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/main.py +9 -0
- rbx_cp-0.5.40/rbx/box/packaging/moj/packager.py +125 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/schema.py +66 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/solutions.py +31 -10
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/state.py +1 -0
- rbx_cp-0.5.40/rbx/box/unit.py +113 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/validators.py +17 -8
- rbx_cp-0.5.40/rbx/resources/packagers/moj/scripts/compare.sh +82 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/LICENSE +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/README.md +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/builder.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/cd.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/compile.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/build_contest_statements.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/contest_package.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/contest_utils.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/contest/statements.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/creation.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/deferred.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/extensions.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/formatting.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/generators.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/generators_test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/lazy_importing_main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/lazy_importing_test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/package.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/contest_main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/retries.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/sanitizers/warning_stack.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/setter_config.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/solutions_test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/build_statements.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/builders.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/stresses.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/testcase_extractors.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/testcase_utils.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/testcases/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/testcases/main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/ui/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/checker.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/clone.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/config.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/console.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/create.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/edit.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/caching.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/sandbox.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/timeit.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/steps.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/main.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.mac.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compare +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/run.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/schema.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/submit.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/test.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/gen1.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/gen2.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/genScript.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/ole.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/problem.rbx.yml +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/re.sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/tests/1.in +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/tle.sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/validator.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/box1/wa.sol.cpp +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/caching/executable.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testdata/compatible +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/testing_utils.py +0 -0
- {rbx_cp-0.5.38 → rbx_cp-0.5.40}/rbx/utils.py +0 -0
@@ -17,11 +17,15 @@ from rbx.grading.steps import (
|
|
17
17
|
Outcome,
|
18
18
|
RunLog,
|
19
19
|
)
|
20
|
+
from rbx.utils import StatusProgress
|
20
21
|
|
21
22
|
|
22
|
-
def compile_checker() -> str:
|
23
|
+
def compile_checker(progress: Optional[StatusProgress] = None) -> str:
|
23
24
|
checker = package.get_checker()
|
24
25
|
|
26
|
+
if progress:
|
27
|
+
progress.update('Compiling checker...')
|
28
|
+
|
25
29
|
try:
|
26
30
|
digest = compile_item(checker, sanitized=SanitizationLevel.PREFER)
|
27
31
|
except Exception as e:
|
@@ -47,7 +47,7 @@ class SanitizationLevel(Enum):
|
|
47
47
|
|
48
48
|
def should_sanitize(self) -> bool:
|
49
49
|
cfg = setter_config.get_setter_config()
|
50
|
-
if cfg.sanitizers.enabled:
|
50
|
+
if cfg.sanitizers.enabled or state.STATE.sanitized:
|
51
51
|
return self.value >= SanitizationLevel.PREFER.value
|
52
52
|
return self.value >= SanitizationLevel.FORCE.value
|
53
53
|
|
@@ -55,43 +55,70 @@ app.add_typer(
|
|
55
55
|
setter_config.app,
|
56
56
|
name='config, cfg',
|
57
57
|
cls=annotations.AliasGroup,
|
58
|
-
help='Manage setter configuration.',
|
58
|
+
help='Manage setter configuration (sub-command).',
|
59
|
+
rich_help_panel='Configuration',
|
59
60
|
)
|
60
61
|
app.add_typer(
|
61
62
|
build_statements.app,
|
62
63
|
name='statements, st',
|
63
64
|
cls=annotations.AliasGroup,
|
64
|
-
help='Manage statements.',
|
65
|
+
help='Manage statements (sub-command).',
|
66
|
+
rich_help_panel='Deploying',
|
65
67
|
)
|
66
68
|
app.add_typer(
|
67
69
|
download.app,
|
68
70
|
name='download',
|
69
71
|
cls=annotations.AliasGroup,
|
70
|
-
help='Download an asset from supported repositories.',
|
72
|
+
help='Download an asset from supported repositories (sub-command).',
|
73
|
+
rich_help_panel='Management',
|
71
74
|
)
|
72
75
|
app.add_typer(
|
73
|
-
presets.app,
|
76
|
+
presets.app,
|
77
|
+
name='presets',
|
78
|
+
cls=annotations.AliasGroup,
|
79
|
+
help='Manage presets (sub-command).',
|
80
|
+
rich_help_panel='Configuration',
|
74
81
|
)
|
75
82
|
app.add_typer(
|
76
83
|
packaging.app,
|
77
84
|
name='package, pkg',
|
78
85
|
cls=annotations.AliasGroup,
|
79
|
-
help='Build problem packages.',
|
86
|
+
help='Build problem packages (sub-command).',
|
87
|
+
rich_help_panel='Deploying',
|
80
88
|
)
|
81
89
|
app.add_typer(
|
82
|
-
contest.app,
|
90
|
+
contest.app,
|
91
|
+
name='contest',
|
92
|
+
cls=annotations.AliasGroup,
|
93
|
+
help='Manage contests (sub-command).',
|
94
|
+
rich_help_panel='Management',
|
83
95
|
)
|
84
96
|
app.add_typer(
|
85
97
|
testcases.app,
|
86
98
|
name='testcases, tc, t',
|
87
99
|
cls=annotations.AliasGroup,
|
88
|
-
help='
|
100
|
+
help='Manage testcases (sub-command).',
|
101
|
+
rich_help_panel='Management',
|
89
102
|
)
|
90
103
|
|
91
104
|
|
92
105
|
@app.callback()
|
93
|
-
def main(
|
106
|
+
def main(
|
107
|
+
sanitized: bool = typer.Option(
|
108
|
+
False,
|
109
|
+
'--sanitized',
|
110
|
+
'-s',
|
111
|
+
help='Whether to compile and run testlib components with sanitizers enabled. '
|
112
|
+
'If you want to run the solutions with sanitizers enabled, use the "-s" flag in the corresponding run command.',
|
113
|
+
),
|
114
|
+
):
|
94
115
|
state.STATE.run_through_cli = True
|
116
|
+
state.STATE.sanitized = sanitized
|
117
|
+
if sanitized:
|
118
|
+
console.console.print(
|
119
|
+
'[warning]Sanitizers are running just for testlib components.\n'
|
120
|
+
'If you want to run the solutions with sanitizers enabled, use the [item]-s[/item] flag in the corresponding run command.[/warning]'
|
121
|
+
)
|
95
122
|
|
96
123
|
|
97
124
|
# @app.command('ui', hidden=True)
|
@@ -100,7 +127,11 @@ def main():
|
|
100
127
|
# ui_pkg.start()
|
101
128
|
|
102
129
|
|
103
|
-
@app.command(
|
130
|
+
@app.command(
|
131
|
+
'edit, e',
|
132
|
+
rich_help_panel='Configuration',
|
133
|
+
help='Open problem.rbx.yml in your default editor.',
|
134
|
+
)
|
104
135
|
@package.within_problem
|
105
136
|
def edit():
|
106
137
|
console.console.print('Opening problem definition in editor...')
|
@@ -110,7 +141,9 @@ def edit():
|
|
110
141
|
config.open_editor(package.find_problem_yaml() or pathlib.Path())
|
111
142
|
|
112
143
|
|
113
|
-
@app.command(
|
144
|
+
@app.command(
|
145
|
+
'build, b', rich_help_panel='Deploying', help='Build all tests for the problem.'
|
146
|
+
)
|
114
147
|
@package.within_problem
|
115
148
|
def build(verification: environment.VerificationParam):
|
116
149
|
from rbx.box import builder
|
@@ -118,7 +151,11 @@ def build(verification: environment.VerificationParam):
|
|
118
151
|
builder.build(verification=verification)
|
119
152
|
|
120
153
|
|
121
|
-
@app.command(
|
154
|
+
@app.command(
|
155
|
+
'run, r',
|
156
|
+
rich_help_panel='Testing',
|
157
|
+
help='Build and run solution(s).',
|
158
|
+
)
|
122
159
|
@package.within_problem
|
123
160
|
def run(
|
124
161
|
verification: environment.VerificationParam,
|
@@ -299,6 +336,7 @@ def _time_impl(check: bool, detailed: bool) -> Optional[int]:
|
|
299
336
|
|
300
337
|
@app.command(
|
301
338
|
'time, t',
|
339
|
+
rich_help_panel='Testing',
|
302
340
|
help='Estimate a time limit for the problem based on a time limit formula and timings of accepted solutions.',
|
303
341
|
)
|
304
342
|
@package.within_problem
|
@@ -333,7 +371,9 @@ def time(
|
|
333
371
|
|
334
372
|
|
335
373
|
@app.command(
|
336
|
-
'irun, ir',
|
374
|
+
'irun, ir',
|
375
|
+
rich_help_panel='Testing',
|
376
|
+
help='Build and run solution(s) by passing testcases in the CLI.',
|
337
377
|
)
|
338
378
|
@package.within_problem
|
339
379
|
def irun(
|
@@ -445,7 +485,11 @@ def irun(
|
|
445
485
|
)
|
446
486
|
|
447
487
|
|
448
|
-
@app.command(
|
488
|
+
@app.command(
|
489
|
+
'create, c',
|
490
|
+
rich_help_panel='Management',
|
491
|
+
help='Create a new problem package.',
|
492
|
+
)
|
449
493
|
def create(
|
450
494
|
name: str,
|
451
495
|
preset: Annotated[
|
@@ -467,7 +511,11 @@ def create(
|
|
467
511
|
creation.create(name)
|
468
512
|
|
469
513
|
|
470
|
-
@app.command(
|
514
|
+
@app.command(
|
515
|
+
'stress',
|
516
|
+
rich_help_panel='Testing',
|
517
|
+
help='Run a stress test.',
|
518
|
+
)
|
471
519
|
@package.within_problem
|
472
520
|
def stress(
|
473
521
|
name: Annotated[
|
@@ -621,7 +669,11 @@ def stress(
|
|
621
669
|
break
|
622
670
|
|
623
671
|
|
624
|
-
@app.command(
|
672
|
+
@app.command(
|
673
|
+
'compile',
|
674
|
+
rich_help_panel='Testing',
|
675
|
+
help='Compile an asset given its path.',
|
676
|
+
)
|
625
677
|
@package.within_problem
|
626
678
|
def compile_command(
|
627
679
|
path: Annotated[
|
@@ -652,7 +704,11 @@ def compile_command(
|
|
652
704
|
compile.any(path, sanitized, warnings)
|
653
705
|
|
654
706
|
|
655
|
-
@app.command(
|
707
|
+
@app.command(
|
708
|
+
'validate',
|
709
|
+
rich_help_panel='Testing',
|
710
|
+
help='Run the validator in a one-off fashion, interactively.',
|
711
|
+
)
|
656
712
|
@package.within_problem
|
657
713
|
def validate(
|
658
714
|
path: Annotated[
|
@@ -685,7 +741,23 @@ def validate(
|
|
685
741
|
validators.print_validation_report([info])
|
686
742
|
|
687
743
|
|
688
|
-
@app.command(
|
744
|
+
@app.command(
|
745
|
+
'unit',
|
746
|
+
rich_help_panel='Testing',
|
747
|
+
help='Run unit tests for the validator and checker.',
|
748
|
+
)
|
749
|
+
def unit_tests():
|
750
|
+
from rbx.box import unit
|
751
|
+
|
752
|
+
with utils.StatusProgress('Running unit tests...') as s:
|
753
|
+
unit.run_unit_tests(s)
|
754
|
+
|
755
|
+
|
756
|
+
@app.command(
|
757
|
+
'environment, env',
|
758
|
+
rich_help_panel='Configuration',
|
759
|
+
help='Set or show the current box environment.',
|
760
|
+
)
|
689
761
|
def environment_command(
|
690
762
|
env: Annotated[Optional[str], typer.Argument()] = None,
|
691
763
|
install_from: Annotated[
|
@@ -730,6 +802,7 @@ def environment_command(
|
|
730
802
|
|
731
803
|
@app.command(
|
732
804
|
'activate',
|
805
|
+
rich_help_panel='Configuration',
|
733
806
|
help='Activate the environment of the current preset used by the package.',
|
734
807
|
)
|
735
808
|
@cd.within_closest_package
|
@@ -764,7 +837,11 @@ def activate():
|
|
764
837
|
console.console.print(f'[success]Preset [item]{preset.name}[/item] is activated.')
|
765
838
|
|
766
839
|
|
767
|
-
@app.command(
|
840
|
+
@app.command(
|
841
|
+
'languages',
|
842
|
+
rich_help_panel='Configuration',
|
843
|
+
help='List the languages available in this environment',
|
844
|
+
)
|
768
845
|
def languages():
|
769
846
|
env = environment.get_environment()
|
770
847
|
|
@@ -780,7 +857,11 @@ def languages():
|
|
780
857
|
console.console.print()
|
781
858
|
|
782
859
|
|
783
|
-
@app.command(
|
860
|
+
@app.command(
|
861
|
+
'clear, clean',
|
862
|
+
rich_help_panel='Management',
|
863
|
+
help='Clears cache and build directories.',
|
864
|
+
)
|
784
865
|
@cd.within_closest_package
|
785
866
|
def clear():
|
786
867
|
console.console.print('Cleaning cache and build directories...')
|
@@ -70,3 +70,12 @@ def boca(
|
|
70
70
|
from rbx.box.packaging.boca.packager import BocaPackager
|
71
71
|
|
72
72
|
run_packager(BocaPackager, verification=verification)
|
73
|
+
|
74
|
+
|
75
|
+
@app.command('moj', help='Build a package for MOJ.')
|
76
|
+
def moj(
|
77
|
+
verification: environment.VerificationParam,
|
78
|
+
):
|
79
|
+
from rbx.box.packaging.moj.packager import MojPackager
|
80
|
+
|
81
|
+
run_packager(MojPackager, verification=verification)
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import pathlib
|
2
|
+
import shutil
|
3
|
+
from typing import List
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from rbx import console
|
8
|
+
from rbx.box import package
|
9
|
+
from rbx.box.environment import get_extension_or_default
|
10
|
+
from rbx.box.packaging.boca.extension import BocaExtension
|
11
|
+
from rbx.box.packaging.boca.packager import BocaPackager
|
12
|
+
from rbx.box.packaging.packager import BuiltStatement
|
13
|
+
from rbx.config import get_default_app_path
|
14
|
+
from rbx.grading.judge.digester import digest_cooperatively
|
15
|
+
|
16
|
+
|
17
|
+
class MojPackager(BocaPackager):
|
18
|
+
def _get_problem_info(self) -> str:
|
19
|
+
statement = self._get_main_statement()
|
20
|
+
return (
|
21
|
+
f'basename={self._get_problem_name()}\n'
|
22
|
+
f'fullname={statement.title}\n'
|
23
|
+
f'descfile={self._get_problem_name()}.pdf\n'
|
24
|
+
)
|
25
|
+
|
26
|
+
def _get_limits(self) -> str:
|
27
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
28
|
+
|
29
|
+
pkg = package.find_problem_package_or_die()
|
30
|
+
tl = pkg.timeLimit
|
31
|
+
ml = pkg.memoryLimit
|
32
|
+
ol = pkg.outputLimit
|
33
|
+
conf = f'ULIMITS[-f]={ol}\n' f'ULIMITS[-v]={ml}\n' f'TL[default]={tl / 1000}\n'
|
34
|
+
for language in extension.languages:
|
35
|
+
conf += f'TL[{language}]={self._get_pkg_timelimit(language) / 1000}\n'
|
36
|
+
return conf
|
37
|
+
|
38
|
+
def _get_compare(self) -> str:
|
39
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
40
|
+
|
41
|
+
compare_path = (
|
42
|
+
get_default_app_path() / 'packagers' / 'moj' / 'scripts' / 'compare.sh'
|
43
|
+
)
|
44
|
+
if not compare_path.exists():
|
45
|
+
console.console.print(
|
46
|
+
'[error]MOJ template compare script not found.[/error]'
|
47
|
+
)
|
48
|
+
raise typer.Exit(1)
|
49
|
+
with package.get_checker().path.open('rb') as f:
|
50
|
+
checker_hash = digest_cooperatively(f)
|
51
|
+
return (
|
52
|
+
compare_path.read_text()
|
53
|
+
.replace('{{rbxFlags}}', extension.flags_with_defaults()['cc'])
|
54
|
+
.replace('{{checkerHash}}', checker_hash)
|
55
|
+
)
|
56
|
+
|
57
|
+
def _get_checker(self) -> str:
|
58
|
+
return package.get_checker().path.read_text()
|
59
|
+
|
60
|
+
def name(self) -> str:
|
61
|
+
return 'moj'
|
62
|
+
|
63
|
+
def package(
|
64
|
+
self,
|
65
|
+
build_path: pathlib.Path,
|
66
|
+
into_path: pathlib.Path,
|
67
|
+
built_statements: List[BuiltStatement],
|
68
|
+
) -> pathlib.Path:
|
69
|
+
# Prepare dummy files
|
70
|
+
author_path = into_path / 'author'
|
71
|
+
author_path.parent.mkdir(parents=True, exist_ok=True)
|
72
|
+
author_path.write_text('Unknown\n')
|
73
|
+
|
74
|
+
tags_path = into_path / 'tags'
|
75
|
+
tags_path.parent.mkdir(parents=True, exist_ok=True)
|
76
|
+
tags_path.write_text('')
|
77
|
+
|
78
|
+
# Prepare limits
|
79
|
+
limits_path = into_path / 'conf'
|
80
|
+
limits_path.parent.mkdir(parents=True, exist_ok=True)
|
81
|
+
limits_path.write_text(self._get_limits())
|
82
|
+
|
83
|
+
# Prepare compare
|
84
|
+
compare_path = into_path / 'scripts' / 'compare.sh'
|
85
|
+
compare_path.parent.mkdir(parents=True, exist_ok=True)
|
86
|
+
compare_path.write_text(self._get_compare())
|
87
|
+
|
88
|
+
# Prepare checker
|
89
|
+
checker_path = into_path / 'scripts' / 'checker.cpp'
|
90
|
+
checker_path.parent.mkdir(parents=True, exist_ok=True)
|
91
|
+
checker_path.write_text(self._get_checker())
|
92
|
+
|
93
|
+
# Problem statement
|
94
|
+
enunciado_path = into_path / 'docs' / 'enunciado.pdf'
|
95
|
+
enunciado_path.parent.mkdir(parents=True, exist_ok=True)
|
96
|
+
shutil.copyfile(
|
97
|
+
self._get_main_built_statement(built_statements).path,
|
98
|
+
enunciado_path,
|
99
|
+
)
|
100
|
+
|
101
|
+
# Copy solutions
|
102
|
+
solutions_path = into_path / 'solutions'
|
103
|
+
solutions_path.mkdir(parents=True, exist_ok=True)
|
104
|
+
self._copy_solutions(solutions_path)
|
105
|
+
|
106
|
+
# Prepare IO
|
107
|
+
inputs_path = into_path / 'tests' / 'input'
|
108
|
+
inputs_path.mkdir(parents=True, exist_ok=True)
|
109
|
+
outputs_path = into_path / 'tests' / 'output'
|
110
|
+
outputs_path.mkdir(parents=True, exist_ok=True)
|
111
|
+
|
112
|
+
testcases = self.get_flattened_built_testcases()
|
113
|
+
for i, testcase in enumerate(testcases):
|
114
|
+
shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
|
115
|
+
if testcase.outputPath is not None:
|
116
|
+
shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
|
117
|
+
else:
|
118
|
+
(outputs_path / f'{i+1:03d}').touch()
|
119
|
+
|
120
|
+
# Zip all.
|
121
|
+
shutil.make_archive(
|
122
|
+
str(build_path / self._get_problem_name()), 'zip', into_path
|
123
|
+
)
|
124
|
+
|
125
|
+
return (build_path / self._get_problem_name()).with_suffix('.zip')
|
@@ -148,6 +148,14 @@ class ExpectedOutcome(AutoEnum):
|
|
148
148
|
return bool(set(self.get_matches()) & set(rhs.get_matches()))
|
149
149
|
|
150
150
|
|
151
|
+
class ValidatorOutcome(AutoEnum):
|
152
|
+
VALID = alias('valid') # type: ignore
|
153
|
+
"""Expected outcome for valid tests."""
|
154
|
+
|
155
|
+
INVALID = alias('invalid') # type: ignore
|
156
|
+
"""Expected outcome for invalid tests."""
|
157
|
+
|
158
|
+
|
151
159
|
class CodeItem(BaseModel):
|
152
160
|
model_config = ConfigDict(extra='forbid')
|
153
161
|
|
@@ -337,6 +345,59 @@ class LimitModifiers(BaseModel):
|
|
337
345
|
)
|
338
346
|
|
339
347
|
|
348
|
+
class ValidatorTest(BaseModel):
|
349
|
+
model_config = ConfigDict(extra='forbid')
|
350
|
+
|
351
|
+
input: pathlib.Path = Field(
|
352
|
+
description='The input file to be used as unit test input for the validator.'
|
353
|
+
)
|
354
|
+
outcome: ValidatorOutcome = Field(
|
355
|
+
default=ValidatorOutcome.VALID,
|
356
|
+
description='The expected outcome of the validator.',
|
357
|
+
)
|
358
|
+
|
359
|
+
validator: Optional[CodeItem] = Field(
|
360
|
+
default=None,
|
361
|
+
description='The validator to use for this test. If not specified, will use the package-level validator.',
|
362
|
+
)
|
363
|
+
|
364
|
+
|
365
|
+
class CheckerTest(BaseModel):
|
366
|
+
model_config = ConfigDict(extra='forbid')
|
367
|
+
|
368
|
+
input: Optional[pathlib.Path] = Field(
|
369
|
+
default=None,
|
370
|
+
description='The input file to be used as unit test input for the checker. If not specified, will pass an empty file.',
|
371
|
+
)
|
372
|
+
output: Optional[pathlib.Path] = Field(
|
373
|
+
default=None,
|
374
|
+
description='The solution output file to be used as unit test output for the checker. If not specified, will pass an empty file.',
|
375
|
+
)
|
376
|
+
answer: Optional[pathlib.Path] = Field(
|
377
|
+
default=None,
|
378
|
+
description='The answer file to be used as unit test answer for the checker. If not specified, will pass an empty file.',
|
379
|
+
)
|
380
|
+
|
381
|
+
outcome: ExpectedOutcome = Field(
|
382
|
+
default=ExpectedOutcome.ACCEPTED,
|
383
|
+
description='The expected outcome of the checker.',
|
384
|
+
)
|
385
|
+
|
386
|
+
|
387
|
+
class UnitTests(BaseModel):
|
388
|
+
model_config = ConfigDict(extra='forbid')
|
389
|
+
|
390
|
+
validator: List[ValidatorTest] = Field(
|
391
|
+
default=[],
|
392
|
+
description='Unit tests for the validator.',
|
393
|
+
)
|
394
|
+
|
395
|
+
checker: List[CheckerTest] = Field(
|
396
|
+
default=[],
|
397
|
+
description='Unit tests for the checker.',
|
398
|
+
)
|
399
|
+
|
400
|
+
|
340
401
|
class Package(BaseModel):
|
341
402
|
model_config = ConfigDict(extra='forbid')
|
342
403
|
|
@@ -399,6 +460,11 @@ that is correct and used as reference -- and should have the `accepted` outcome.
|
|
399
460
|
default={}, description='Variables to be re-used across the package.'
|
400
461
|
)
|
401
462
|
|
463
|
+
unitTests: UnitTests = Field(
|
464
|
+
default_factory=UnitTests,
|
465
|
+
description='Unit tests for components of this problem.',
|
466
|
+
)
|
467
|
+
|
402
468
|
@property
|
403
469
|
def expanded_vars(self) -> Dict[str, Primitive]:
|
404
470
|
return {key: expand_var(value) for key, value in self.vars.items()}
|
@@ -635,13 +635,22 @@ async def run_and_print_interactive_solutions(
|
|
635
635
|
|
636
636
|
for item in items:
|
637
637
|
sol = pkg.solutions[item.solution_index]
|
638
|
-
|
639
|
-
|
638
|
+
|
639
|
+
if progress:
|
640
|
+
progress.update(f'Running [item]{sol.path}[/item]...')
|
640
641
|
|
641
642
|
eval = await item.eval()
|
642
643
|
|
644
|
+
with utils.no_progress(progress):
|
645
|
+
console.console.print(get_testcase_markup_verdict(eval), end=' ')
|
646
|
+
_print_solution_header(sol, console.console, is_irun=True)
|
647
|
+
_print_solution_outcome(
|
648
|
+
sol, [eval], console.console, verification, subset=True
|
649
|
+
)
|
650
|
+
|
643
651
|
stdout_path = eval.log.stdout_absolute_path
|
644
652
|
if print:
|
653
|
+
console.console.rule('Output', style='status')
|
645
654
|
if (
|
646
655
|
eval.testcase.output is not None
|
647
656
|
and stdout_path is not None
|
@@ -780,14 +789,17 @@ def _print_solution_outcome(
|
|
780
789
|
evals: List[Evaluation],
|
781
790
|
console: rich.console.Console,
|
782
791
|
verification: VerificationLevel = VerificationLevel.NONE,
|
792
|
+
subset: bool = False,
|
783
793
|
) -> bool:
|
784
794
|
pkg = package.find_problem_package_or_die()
|
785
795
|
|
786
796
|
has_plain_tle = False
|
797
|
+
all_verdicts = set()
|
787
798
|
bad_verdicts = set()
|
788
799
|
no_tle_bad_verdicts = set()
|
789
800
|
has_sanitizer_warnings = False
|
790
801
|
for eval in evals:
|
802
|
+
all_verdicts.add(eval.result.outcome)
|
791
803
|
if eval.result.outcome != Outcome.ACCEPTED:
|
792
804
|
bad_verdicts.add(eval.result.outcome)
|
793
805
|
if (
|
@@ -808,18 +820,27 @@ def _print_solution_outcome(
|
|
808
820
|
matched_bad_verdicts = bad_verdicts - unmatched_bad_verdicts
|
809
821
|
expected_outcome_is_bad = not solution.outcome.match(Outcome.ACCEPTED)
|
810
822
|
|
811
|
-
|
823
|
+
has_failed = unmatched_bad_verdicts or (
|
824
|
+
expected_outcome_is_bad and not matched_bad_verdicts and not subset
|
825
|
+
)
|
826
|
+
if has_failed:
|
812
827
|
console.print('[error]FAILED[/error]', end=' ')
|
813
828
|
else:
|
814
829
|
console.print('[success]OK[/success]', end=' ')
|
815
830
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
console.print(f'
|
821
|
-
|
822
|
-
|
831
|
+
if has_failed or not subset:
|
832
|
+
console.print(f'Expected: {solution.outcome}', end='')
|
833
|
+
elif subset:
|
834
|
+
all_verdicts_names = ' '.join(v.name for v in all_verdicts)
|
835
|
+
console.print(f'Got: {all_verdicts_names}', end='')
|
836
|
+
|
837
|
+
if has_failed or not subset:
|
838
|
+
# Only print verdicts if not subset.
|
839
|
+
if unmatched_bad_verdicts:
|
840
|
+
unmatched_bad_verdicts_names = set(v.name for v in unmatched_bad_verdicts)
|
841
|
+
console.print(f', got: {" ".join(unmatched_bad_verdicts_names)}', end='')
|
842
|
+
elif expected_outcome_is_bad and not matched_bad_verdicts and not subset:
|
843
|
+
console.print(f', got: {Outcome.ACCEPTED.name}', end='')
|
823
844
|
|
824
845
|
console.print()
|
825
846
|
evals_time = _get_evals_time_in_ms(evals)
|