rbx.cp 0.5.48__tar.gz → 0.5.50__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.48 → rbx_cp-0.5.50}/PKG-INFO +1 -1
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/pyproject.toml +1 -1
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/cli.py +1 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/main.py +3 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/schema.py +8 -13
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/solutions.py +40 -11
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/testcase_utils.py +12 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/testcases/main.py +2 -0
- rbx_cp-0.5.50/rbx/box/unit.py +206 -0
- rbx_cp-0.5.48/rbx/box/unit.py +0 -116
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/LICENSE +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/README.md +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/builder.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/cd.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/checkers.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/code.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/compile.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/build_contest_statements.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/contest_package.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/contest_utils.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/contest/statements.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/creation.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/deferred.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/extensions.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/formatting.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/generators.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/generators_test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/lazy_importing_main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/lazy_importing_test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/naming.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/package.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/contest_main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/moj/packager.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/retries.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/sanitizers/warning_stack.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/setter_config.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/solutions_test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/state.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/build_statements.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/builders.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/stresses.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/tasks.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/testcase_extractors.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/testcases/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/ui/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/validators.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/checker.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/clone.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/config.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/console.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/create.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/edit.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/caching.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/sandbox.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/sandboxes/timeit.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/processing_context.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/steps.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/steps_with_caching.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/main.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/default_setter_config.mac.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/default_setter_config.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compare.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/c +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/cc +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/java +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/kt +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/py2 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactive/py3 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/bkp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/run.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/schema.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/submit.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/test.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/testing_utils.py +0 -0
- {rbx_cp-0.5.48 → rbx_cp-0.5.50}/rbx/utils.py +0 -0
@@ -64,6 +64,7 @@ async def run_packager(
|
|
64
64
|
|
65
65
|
|
66
66
|
@app.command('polygon', help='Build a package for Polygon.')
|
67
|
+
@package.within_problem
|
67
68
|
@syncer.sync
|
68
69
|
async def polygon(
|
69
70
|
verification: environment.VerificationParam,
|
@@ -74,6 +75,7 @@ async def polygon(
|
|
74
75
|
|
75
76
|
|
76
77
|
@app.command('boca', help='Build a package for BOCA.')
|
78
|
+
@package.within_problem
|
77
79
|
@syncer.sync
|
78
80
|
async def boca(
|
79
81
|
verification: environment.VerificationParam,
|
@@ -84,6 +86,7 @@ async def boca(
|
|
84
86
|
|
85
87
|
|
86
88
|
@app.command('moj', help='Build a package for MOJ.')
|
89
|
+
@package.within_problem
|
87
90
|
@syncer.sync
|
88
91
|
async def moj(
|
89
92
|
verification: environment.VerificationParam,
|
@@ -356,8 +356,8 @@ class LimitModifiers(BaseModel):
|
|
356
356
|
class ValidatorTest(BaseModel):
|
357
357
|
model_config = ConfigDict(extra='forbid')
|
358
358
|
|
359
|
-
|
360
|
-
description='
|
359
|
+
glob: str = Field(
|
360
|
+
description='A glob pattern for the input files to be used as unit test input for the validator.'
|
361
361
|
)
|
362
362
|
outcome: ValidatorOutcome = Field(
|
363
363
|
default=ValidatorOutcome.VALID,
|
@@ -373,17 +373,12 @@ class ValidatorTest(BaseModel):
|
|
373
373
|
class CheckerTest(BaseModel):
|
374
374
|
model_config = ConfigDict(extra='forbid')
|
375
375
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
description='The solution output file to be used as unit test output for the checker. If not specified, will pass an empty file.',
|
383
|
-
)
|
384
|
-
answer: Optional[pathlib.Path] = Field(
|
385
|
-
default=None,
|
386
|
-
description='The answer file to be used as unit test answer for the checker. If not specified, will pass an empty file.',
|
376
|
+
glob: str = Field(
|
377
|
+
description="""
|
378
|
+
A glob pattern for the files to be used as unit test input for the checker.
|
379
|
+
This glob should simultaneously match the input, output, and answer files (.in, .out, .ans).
|
380
|
+
If one of them is not present, an empty file will be used instead.
|
381
|
+
""",
|
387
382
|
)
|
388
383
|
|
389
384
|
outcome: ExpectedOutcome = Field(
|
@@ -47,7 +47,12 @@ from rbx.box.tasks import (
|
|
47
47
|
run_solution_on_testcase,
|
48
48
|
)
|
49
49
|
from rbx.box.testcase_extractors import extract_generation_testcases
|
50
|
-
from rbx.box.testcase_utils import
|
50
|
+
from rbx.box.testcase_utils import (
|
51
|
+
TestcaseEntry,
|
52
|
+
find_built_testcases,
|
53
|
+
parse_interaction,
|
54
|
+
print_interaction,
|
55
|
+
)
|
51
56
|
from rbx.grading.steps import (
|
52
57
|
Evaluation,
|
53
58
|
Outcome,
|
@@ -323,6 +328,19 @@ def _produce_solution_items(
|
|
323
328
|
return res
|
324
329
|
|
325
330
|
|
331
|
+
def print_best_output(output_files: List[pathlib.Path], empty_warning: bool = False):
|
332
|
+
for output_file in output_files:
|
333
|
+
if not output_file.is_file():
|
334
|
+
continue
|
335
|
+
if output_file.suffix == '.pio':
|
336
|
+
print_interaction(parse_interaction(output_file))
|
337
|
+
else:
|
338
|
+
console.console.print(output_file.read_text())
|
339
|
+
return
|
340
|
+
if empty_warning:
|
341
|
+
console.console.print('[warning]Solution produced no output.[/warning]')
|
342
|
+
|
343
|
+
|
326
344
|
def run_solutions(
|
327
345
|
progress: Optional[StatusProgress] = None,
|
328
346
|
tracked_solutions: Optional[Set[str]] = None,
|
@@ -528,6 +546,7 @@ def _run_interactive_solutions(
|
|
528
546
|
output_dir=output_dir,
|
529
547
|
interactor_digest=interactor_digest,
|
530
548
|
verification=verification,
|
549
|
+
capture_pipes=True,
|
531
550
|
)
|
532
551
|
|
533
552
|
yield EvaluationItem(
|
@@ -589,18 +608,28 @@ async def run_and_print_interactive_solutions(
|
|
589
608
|
)
|
590
609
|
|
591
610
|
stdout_path = eval.log.stdout_absolute_path
|
592
|
-
if print:
|
611
|
+
if print and stdout_path is not None:
|
612
|
+
if pkg.type == TaskType.COMMUNICATION:
|
613
|
+
console.console.rule('Interaction', style='status')
|
614
|
+
output_files = [
|
615
|
+
stdout_path.with_suffix('.pio'),
|
616
|
+
stdout_path.with_suffix('.pout'),
|
617
|
+
]
|
618
|
+
print_best_output(output_files, empty_warning=True)
|
619
|
+
|
593
620
|
console.console.rule('Output', style='status')
|
594
|
-
|
595
|
-
|
596
|
-
and stdout_path is not None
|
597
|
-
and stdout_path.is_file()
|
598
|
-
):
|
599
|
-
console.console.print(stdout_path.read_text())
|
600
|
-
else:
|
601
|
-
console.console.print('[warning]Solution produced no output.[/warning]')
|
621
|
+
output_files = [stdout_path]
|
622
|
+
print_best_output(output_files, empty_warning=True)
|
602
623
|
elif stdout_path is not None:
|
603
|
-
|
624
|
+
if stdout_path.with_suffix('.pout').is_file():
|
625
|
+
stdout_path = stdout_path.with_suffix('.pout')
|
626
|
+
|
627
|
+
if stdout_path.is_file():
|
628
|
+
console.console.print(f'[status]Output:[/status] {stdout_path}')
|
629
|
+
if stdout_path.with_suffix('.pio').is_file():
|
630
|
+
console.console.print(
|
631
|
+
f'[status]Interaction:[/status] {stdout_path.with_suffix(".pio")}'
|
632
|
+
)
|
604
633
|
if eval.log.stderr_absolute_path is not None:
|
605
634
|
console.console.print(
|
606
635
|
f'[status]Stderr:[/status] {eval.log.stderr_absolute_path}'
|
@@ -2,6 +2,8 @@ import pathlib
|
|
2
2
|
import shutil
|
3
3
|
from typing import List, Optional, Tuple
|
4
4
|
|
5
|
+
import rich
|
6
|
+
import rich.text
|
5
7
|
import typer
|
6
8
|
from pydantic import BaseModel
|
7
9
|
|
@@ -209,3 +211,13 @@ def parse_interaction(file: pathlib.Path) -> TestcaseInteraction:
|
|
209
211
|
prefixes=(interactor_prefix, solution_prefix),
|
210
212
|
entries=entries,
|
211
213
|
)
|
214
|
+
|
215
|
+
|
216
|
+
def print_interaction(interaction: TestcaseInteraction):
|
217
|
+
for entry in interaction.entries:
|
218
|
+
text = rich.text.Text(entry.data)
|
219
|
+
if entry.pipe == 0:
|
220
|
+
text.stylize('status')
|
221
|
+
else:
|
222
|
+
text.stylize('info')
|
223
|
+
console.console.print(text, end='')
|
@@ -90,6 +90,7 @@ async def _generate_for_editing(
|
|
90
90
|
|
91
91
|
|
92
92
|
@app.command('view, v', help='View a testcase in your default editor.')
|
93
|
+
@package.within_problem
|
93
94
|
@syncer.sync
|
94
95
|
async def view(
|
95
96
|
tc: Annotated[
|
@@ -126,6 +127,7 @@ async def view(
|
|
126
127
|
|
127
128
|
|
128
129
|
@app.command('info, i', help='Show information about testcases.')
|
130
|
+
@package.within_problem
|
129
131
|
@syncer.sync
|
130
132
|
async def info(
|
131
133
|
pattern: Annotated[
|
@@ -0,0 +1,206 @@
|
|
1
|
+
import pathlib
|
2
|
+
from typing import List, Optional, Set
|
3
|
+
|
4
|
+
import syncer
|
5
|
+
from pydantic import BaseModel
|
6
|
+
|
7
|
+
from rbx import console
|
8
|
+
from rbx.box import checkers, package, validators
|
9
|
+
from rbx.box.schema import (
|
10
|
+
CheckerTest,
|
11
|
+
CodeItem,
|
12
|
+
ExpectedOutcome,
|
13
|
+
Testcase,
|
14
|
+
ValidatorOutcome,
|
15
|
+
ValidatorTest,
|
16
|
+
)
|
17
|
+
from rbx.utils import StatusProgress
|
18
|
+
|
19
|
+
|
20
|
+
class ValidatorTestEntry(BaseModel):
|
21
|
+
input: pathlib.Path
|
22
|
+
outcome: ValidatorOutcome
|
23
|
+
validator: Optional[CodeItem]
|
24
|
+
|
25
|
+
|
26
|
+
class CheckerTestEntry(BaseModel):
|
27
|
+
input: Optional[pathlib.Path] = None
|
28
|
+
output: Optional[pathlib.Path] = None
|
29
|
+
answer: Optional[pathlib.Path] = None
|
30
|
+
outcome: ExpectedOutcome
|
31
|
+
|
32
|
+
def running_tests_formatted_string(self) -> str:
|
33
|
+
res = []
|
34
|
+
if self.input:
|
35
|
+
res.append(f'[item]{self.input}[/item]')
|
36
|
+
if self.output:
|
37
|
+
res.append(f'[item]{self.output}[/item]')
|
38
|
+
if self.answer:
|
39
|
+
res.append(f'[item]{self.answer}[/item]')
|
40
|
+
return ', '.join(res)
|
41
|
+
|
42
|
+
|
43
|
+
def _extract_validator_test_entries(
|
44
|
+
tests: List[ValidatorTest],
|
45
|
+
) -> List[ValidatorTestEntry]:
|
46
|
+
res: List[ValidatorTestEntry] = []
|
47
|
+
for test in tests:
|
48
|
+
for input in pathlib.Path().glob(str(test.glob)):
|
49
|
+
if not input.is_file():
|
50
|
+
continue
|
51
|
+
res.append(
|
52
|
+
ValidatorTestEntry(
|
53
|
+
input=input, outcome=test.outcome, validator=test.validator
|
54
|
+
)
|
55
|
+
)
|
56
|
+
return sorted(res, key=lambda x: x.input.name)
|
57
|
+
|
58
|
+
|
59
|
+
def _extract_checker_test_entries(tests: List[CheckerTest]) -> List[CheckerTestEntry]:
|
60
|
+
res: List[CheckerTestEntry] = []
|
61
|
+
seen: Set[pathlib.Path] = set()
|
62
|
+
for test in tests:
|
63
|
+
for file in pathlib.Path().glob(str(test.glob)):
|
64
|
+
if not file.is_file():
|
65
|
+
continue
|
66
|
+
if file.suffix not in ['.in', '.out', '.ans']:
|
67
|
+
continue
|
68
|
+
basefile = file.with_suffix('')
|
69
|
+
if basefile in seen:
|
70
|
+
continue
|
71
|
+
seen.add(basefile)
|
72
|
+
input = basefile.with_suffix('.in')
|
73
|
+
output = basefile.with_suffix('.out')
|
74
|
+
answer = basefile.with_suffix('.ans')
|
75
|
+
res.append(
|
76
|
+
CheckerTestEntry(
|
77
|
+
input=input if input.is_file() else None,
|
78
|
+
output=output if output.is_file() else None,
|
79
|
+
answer=answer if answer.is_file() else None,
|
80
|
+
outcome=test.outcome,
|
81
|
+
)
|
82
|
+
)
|
83
|
+
return res
|
84
|
+
|
85
|
+
|
86
|
+
def _get_validator_for_test(test: ValidatorTestEntry) -> Optional[CodeItem]:
|
87
|
+
pkg = package.find_problem_package_or_die()
|
88
|
+
if test.validator is not None:
|
89
|
+
return test.validator
|
90
|
+
return pkg.validator
|
91
|
+
|
92
|
+
|
93
|
+
async def run_validator_unit_tests(progress: StatusProgress):
|
94
|
+
pkg = package.find_problem_package_or_die()
|
95
|
+
|
96
|
+
entries = _extract_validator_test_entries(pkg.unitTests.validator)
|
97
|
+
|
98
|
+
vals: List[CodeItem] = []
|
99
|
+
for test in entries:
|
100
|
+
val = _get_validator_for_test(test)
|
101
|
+
if val is not None:
|
102
|
+
vals.append(val)
|
103
|
+
|
104
|
+
console.console.rule('Validator tests', style='info')
|
105
|
+
if not entries:
|
106
|
+
console.console.print(']No validator unit tests found.')
|
107
|
+
return
|
108
|
+
|
109
|
+
compiled_validators = validators.compile_validators(vals, progress=progress)
|
110
|
+
|
111
|
+
if progress:
|
112
|
+
progress.update('Running validator unit tests...')
|
113
|
+
|
114
|
+
for i, test in enumerate(entries):
|
115
|
+
val = _get_validator_for_test(test)
|
116
|
+
if val is None:
|
117
|
+
console.console.print(
|
118
|
+
f'[warning]No validator found for test [item]#{i + 1}[/item], skipping.[/warning]'
|
119
|
+
)
|
120
|
+
continue
|
121
|
+
|
122
|
+
compiled_digest = compiled_validators[str(val.path)]
|
123
|
+
info = await validators.validate_one_off(
|
124
|
+
test.input,
|
125
|
+
val,
|
126
|
+
compiled_digest,
|
127
|
+
)
|
128
|
+
|
129
|
+
is_valid = test.outcome == ValidatorOutcome.VALID
|
130
|
+
|
131
|
+
markup = (
|
132
|
+
'[success]OK[/success]' if info.ok == is_valid else '[error]FAIL[/error]'
|
133
|
+
)
|
134
|
+
|
135
|
+
console.console.print(
|
136
|
+
f'{markup} Unit test [item]#{i + 1}[/item] for [item]{test.input}[/item]'
|
137
|
+
)
|
138
|
+
console.console.print(f' [status]Expected[/status] {test.outcome.value}')
|
139
|
+
if info.ok != is_valid:
|
140
|
+
if info.ok:
|
141
|
+
console.console.print(' [status]Actual[/status] VALID')
|
142
|
+
else:
|
143
|
+
console.console.print(' [status]Actual[/status] INVALID')
|
144
|
+
|
145
|
+
if info.message:
|
146
|
+
console.console.print(f' [status]Message[/status] {info.message}')
|
147
|
+
|
148
|
+
|
149
|
+
async def run_checker_unit_tests(progress: StatusProgress):
|
150
|
+
pkg = package.find_problem_package_or_die()
|
151
|
+
if not pkg.unitTests.checker:
|
152
|
+
return
|
153
|
+
|
154
|
+
if not package.get_checker():
|
155
|
+
console.console.print(
|
156
|
+
'[warning]No checker found, skipping checker unit tests.[/warning]'
|
157
|
+
)
|
158
|
+
return
|
159
|
+
|
160
|
+
console.console.rule('Checker tests', style='info')
|
161
|
+
|
162
|
+
entries = _extract_checker_test_entries(pkg.unitTests.checker)
|
163
|
+
if not entries:
|
164
|
+
console.console.print('No checker unit tests found.')
|
165
|
+
return
|
166
|
+
|
167
|
+
compiled_digest = checkers.compile_checker(progress=progress)
|
168
|
+
|
169
|
+
if progress:
|
170
|
+
progress.update('Running checker unit tests...')
|
171
|
+
|
172
|
+
empty_file = package.get_empty_sentinel_path()
|
173
|
+
|
174
|
+
for i, test in enumerate(entries):
|
175
|
+
result = await checkers.check(
|
176
|
+
compiled_digest,
|
177
|
+
run_log=None,
|
178
|
+
testcase=Testcase(
|
179
|
+
inputPath=test.input or empty_file,
|
180
|
+
outputPath=test.answer or empty_file,
|
181
|
+
),
|
182
|
+
program_output=test.output or empty_file,
|
183
|
+
skip_run_log=True,
|
184
|
+
)
|
185
|
+
|
186
|
+
markup = (
|
187
|
+
'[success]OK[/success]'
|
188
|
+
if test.outcome.match(result.outcome)
|
189
|
+
else '[error]FAIL[/error]'
|
190
|
+
)
|
191
|
+
|
192
|
+
console.console.print(
|
193
|
+
f'{markup} Unit test [item]#{i + 1}[/item] ({test.running_tests_formatted_string()})'
|
194
|
+
)
|
195
|
+
console.console.print(f' [status]Expected[/status] {test.outcome.name}')
|
196
|
+
|
197
|
+
if not test.outcome.match(result.outcome):
|
198
|
+
console.console.print(f' [status]Actual[/status] {result.outcome.name}')
|
199
|
+
if result.message:
|
200
|
+
console.console.print(f' [status]Message[/status] {result.message}')
|
201
|
+
|
202
|
+
|
203
|
+
@syncer.sync
|
204
|
+
async def run_unit_tests(progress: StatusProgress):
|
205
|
+
await run_validator_unit_tests(progress)
|
206
|
+
await run_checker_unit_tests(progress)
|
rbx_cp-0.5.48/rbx/box/unit.py
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
from typing import List, Optional
|
2
|
-
|
3
|
-
import syncer
|
4
|
-
|
5
|
-
from rbx import console
|
6
|
-
from rbx.box import checkers, package, validators
|
7
|
-
from rbx.box.schema import CodeItem, Testcase, ValidatorOutcome, ValidatorTest
|
8
|
-
from rbx.utils import StatusProgress
|
9
|
-
|
10
|
-
|
11
|
-
def _get_validator_for_test(test: ValidatorTest) -> Optional[CodeItem]:
|
12
|
-
pkg = package.find_problem_package_or_die()
|
13
|
-
if test.validator is not None:
|
14
|
-
return test.validator
|
15
|
-
return pkg.validator
|
16
|
-
|
17
|
-
|
18
|
-
async def run_validator_unit_tests(progress: StatusProgress):
|
19
|
-
pkg = package.find_problem_package_or_die()
|
20
|
-
|
21
|
-
vals: List[CodeItem] = []
|
22
|
-
for test in pkg.unitTests.validator:
|
23
|
-
val = _get_validator_for_test(test)
|
24
|
-
if val is not None:
|
25
|
-
vals.append(val)
|
26
|
-
|
27
|
-
compiled_validators = validators.compile_validators(vals, progress=progress)
|
28
|
-
|
29
|
-
if progress:
|
30
|
-
progress.update('Running validator unit tests...')
|
31
|
-
|
32
|
-
console.console.rule('Validator tests', style='info')
|
33
|
-
|
34
|
-
for i, test in enumerate(pkg.unitTests.validator):
|
35
|
-
val = _get_validator_for_test(test)
|
36
|
-
if val is None:
|
37
|
-
console.console.print(
|
38
|
-
f'[warning]No validator found for test [item]#{i + 1}[/item], skipping.[/warning]'
|
39
|
-
)
|
40
|
-
continue
|
41
|
-
|
42
|
-
compiled_digest = compiled_validators[str(val.path)]
|
43
|
-
info = await validators.validate_one_off(
|
44
|
-
test.input,
|
45
|
-
val,
|
46
|
-
compiled_digest,
|
47
|
-
)
|
48
|
-
|
49
|
-
is_valid = test.outcome == ValidatorOutcome.VALID
|
50
|
-
|
51
|
-
markup = (
|
52
|
-
'[success]OK[/success]' if info.ok == is_valid else '[error]FAIL[/error]'
|
53
|
-
)
|
54
|
-
|
55
|
-
console.console.print(
|
56
|
-
f'{markup} Unit test [item]#{i + 1}[/item] for [item]{test.input}[/item]'
|
57
|
-
)
|
58
|
-
console.console.print(f' [status]Expected[/status] {test.outcome.value}')
|
59
|
-
if info.ok != is_valid:
|
60
|
-
if info.ok:
|
61
|
-
console.console.print(' [status]Actual[/status] VALID')
|
62
|
-
else:
|
63
|
-
console.console.print(f' [status]Actual[/status] {info.message}')
|
64
|
-
|
65
|
-
|
66
|
-
async def run_checker_unit_tests(progress: StatusProgress):
|
67
|
-
pkg = package.find_problem_package_or_die()
|
68
|
-
if not pkg.unitTests.checker:
|
69
|
-
return
|
70
|
-
|
71
|
-
if not package.get_checker():
|
72
|
-
console.console.print(
|
73
|
-
'[warning]No checker found, skipping checker unit tests.[/warning]'
|
74
|
-
)
|
75
|
-
return
|
76
|
-
|
77
|
-
compiled_digest = checkers.compile_checker(progress=progress)
|
78
|
-
|
79
|
-
if progress:
|
80
|
-
progress.update('Running checker unit tests...')
|
81
|
-
|
82
|
-
console.console.rule('Checker tests', style='info')
|
83
|
-
|
84
|
-
empty_file = package.get_empty_sentinel_path()
|
85
|
-
|
86
|
-
for i, test in enumerate(pkg.unitTests.checker):
|
87
|
-
result = await checkers.check(
|
88
|
-
compiled_digest,
|
89
|
-
run_log=None,
|
90
|
-
testcase=Testcase(
|
91
|
-
inputPath=test.input or empty_file,
|
92
|
-
outputPath=test.answer or empty_file,
|
93
|
-
),
|
94
|
-
program_output=test.output or empty_file,
|
95
|
-
skip_run_log=True,
|
96
|
-
)
|
97
|
-
|
98
|
-
markup = (
|
99
|
-
'[success]OK[/success]'
|
100
|
-
if test.outcome.match(result.outcome)
|
101
|
-
else '[error]FAIL[/error]'
|
102
|
-
)
|
103
|
-
|
104
|
-
console.console.print(f'{markup} Unit test [item]#{i + 1}[/item]')
|
105
|
-
console.console.print(f' [status]Expected[/status] {test.outcome.name}')
|
106
|
-
|
107
|
-
if not test.outcome.match(result.outcome):
|
108
|
-
console.console.print(f' [status]Actual[/status] {result.outcome.name}')
|
109
|
-
if result.message:
|
110
|
-
console.console.print(f' [status]Message[/status] {result.message}')
|
111
|
-
|
112
|
-
|
113
|
-
@syncer.sync
|
114
|
-
async def run_unit_tests(progress: StatusProgress):
|
115
|
-
await run_validator_unit_tests(progress)
|
116
|
-
await run_checker_unit_tests(progress)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|