rbx.cp 0.5.35__tar.gz → 0.5.37__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.35 → rbx_cp-0.5.37}/PKG-INFO +1 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/pyproject.toml +1 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/builder.py +5 -2
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/main.py +2 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/creation.py +7 -12
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/generators.py +17 -318
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/generators_test.py +1 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/main.py +10 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/package.py +2 -2
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/schema.py +8 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/solutions.py +1 -1
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/solutions_test.py +1 -1
- rbx_cp-0.5.37/rbx/box/testcase_extractors.py +348 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/testcase_utils.py +17 -7
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/testcases/main.py +8 -6
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/validators.py +61 -33
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/config.py +16 -2
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/LICENSE +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/README.md +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/cd.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/checkers.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/code.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/compile.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/build_contest_statements.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/contest_package.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/contest_utils.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/contest/statements.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/deferred.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/extensions.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/formatting.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/contest_main.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/main.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/retries.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/sanitizers/warning_stack.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/setter_config.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/state.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/build_statements.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/builders.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/stresses.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/testcases/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/ui/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/checker.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/clone.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/console.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/create.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/edit.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/caching.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/sandbox.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/sandboxes/timeit.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/steps.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/steps_with_caching.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/main.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/default_setter_config.mac.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/default_setter_config.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compare +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/run.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/schema.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/submit.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/test.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/gen1.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/gen2.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/genScript.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/ole.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/problem.rbx.yml +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/re.sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/tests/1.in +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/tle.sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/validator.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/box1/wa.sol.cpp +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/caching/executable.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testdata/compatible +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/testing_utils.py +0 -0
- {rbx_cp-0.5.35 → rbx_cp-0.5.37}/rbx/utils.py +0 -0
@@ -4,7 +4,6 @@ from rbx import console, utils
|
|
4
4
|
from rbx.box import environment, package
|
5
5
|
from rbx.box.environment import VerificationLevel
|
6
6
|
from rbx.box.generators import (
|
7
|
-
extract_generation_testcases_from_groups,
|
8
7
|
generate_outputs_for_testcases,
|
9
8
|
generate_testcases,
|
10
9
|
)
|
@@ -13,6 +12,7 @@ from rbx.box.solutions import (
|
|
13
12
|
print_run_report,
|
14
13
|
run_solutions,
|
15
14
|
)
|
15
|
+
from rbx.box.testcase_extractors import extract_generation_testcases_from_groups
|
16
16
|
from rbx.box.validators import (
|
17
17
|
has_validation_errors,
|
18
18
|
print_validation_report,
|
@@ -50,7 +50,10 @@ def build(
|
|
50
50
|
'Validated [item]{processed}[/item] testcases...',
|
51
51
|
keep=True,
|
52
52
|
) as s:
|
53
|
-
infos = validate_testcases(
|
53
|
+
infos = validate_testcases(
|
54
|
+
s,
|
55
|
+
groups=groups,
|
56
|
+
)
|
54
57
|
print_validation_report(infos)
|
55
58
|
|
56
59
|
if has_validation_errors(infos):
|
@@ -128,7 +128,8 @@ def edit():
|
|
128
128
|
@app.command('add, a', help='Add new problem to contest.')
|
129
129
|
@within_contest
|
130
130
|
def add(path: str, short_name: str, preset: Optional[str] = None):
|
131
|
-
|
131
|
+
problem_path = pathlib.Path(path)
|
132
|
+
name = problem_path.stem
|
132
133
|
utils.validate_field(ContestProblem, 'short_name', short_name)
|
133
134
|
utils.validate_field(Package, 'name', name)
|
134
135
|
|
@@ -4,9 +4,8 @@ from typing import Annotated, Optional
|
|
4
4
|
|
5
5
|
import typer
|
6
6
|
|
7
|
-
from rbx import console
|
8
|
-
from rbx.box import presets
|
9
|
-
from rbx.box.contest.contest_package import find_contest_yaml
|
7
|
+
from rbx import console, utils
|
8
|
+
from rbx.box import package, presets
|
10
9
|
from rbx.box.presets.fetch import get_preset_fetch_info
|
11
10
|
|
12
11
|
|
@@ -27,15 +26,6 @@ def create(
|
|
27
26
|
] = None,
|
28
27
|
path: Optional[pathlib.Path] = None,
|
29
28
|
):
|
30
|
-
if find_contest_yaml() is not None:
|
31
|
-
console.console.print(
|
32
|
-
'[error]Cannot [item]rbx create[/item] a problem inside a contest.[/error]'
|
33
|
-
)
|
34
|
-
console.console.print(
|
35
|
-
'[error]Instead, use [item]rbx contest add[/item] to add a problem to a contest.[/error]'
|
36
|
-
)
|
37
|
-
raise typer.Exit(1)
|
38
|
-
|
39
29
|
preset = preset or 'default'
|
40
30
|
console.console.print(f'Creating new problem [item]{name}[/item]...')
|
41
31
|
|
@@ -80,4 +70,9 @@ def create(
|
|
80
70
|
for lock in dest_path.rglob('.preset-lock.yml'):
|
81
71
|
lock.unlink(missing_ok=True)
|
82
72
|
|
73
|
+
# Change problem name.
|
74
|
+
ru, problem = package.get_ruyaml(dest_path)
|
75
|
+
problem['name'] = name
|
76
|
+
utils.save_ruyaml(dest_path / 'problem.rbx.yml', ru, problem)
|
77
|
+
|
83
78
|
presets.generate_lock(preset, root=dest_path)
|
@@ -1,12 +1,8 @@
|
|
1
|
-
import abc
|
2
1
|
import pathlib
|
3
|
-
import shlex
|
4
2
|
import shutil
|
5
|
-
from
|
6
|
-
from typing import Dict, Iterable, List, Optional, Set, Tuple
|
3
|
+
from typing import Dict, List, Optional, Set
|
7
4
|
|
8
5
|
import typer
|
9
|
-
from pydantic import BaseModel
|
10
6
|
|
11
7
|
from rbx import console
|
12
8
|
from rbx.box import checkers, package, testcase_utils, validators
|
@@ -19,10 +15,20 @@ from rbx.box.schema import (
|
|
19
15
|
CodeItem,
|
20
16
|
GeneratorCall,
|
21
17
|
Testcase,
|
22
|
-
TestcaseSubgroup,
|
23
18
|
)
|
24
19
|
from rbx.box.stressing import generator_parser
|
25
|
-
from rbx.box.
|
20
|
+
from rbx.box.testcase_extractors import (
|
21
|
+
GenerationMetadata,
|
22
|
+
GenerationTestcaseEntry,
|
23
|
+
TestcaseGroupVisitor,
|
24
|
+
extract_generation_testcases,
|
25
|
+
run_testcase_visitor,
|
26
|
+
)
|
27
|
+
from rbx.box.testcase_utils import (
|
28
|
+
TestcaseEntry,
|
29
|
+
fill_output_for_defined_testcase,
|
30
|
+
find_built_testcases,
|
31
|
+
)
|
26
32
|
from rbx.grading.steps import (
|
27
33
|
DigestHolder,
|
28
34
|
DigestOrDest,
|
@@ -35,33 +41,11 @@ def _compile_generator(generator: CodeItem) -> str:
|
|
35
41
|
return compile_item(generator, sanitized=SanitizationLevel.PREFER)
|
36
42
|
|
37
43
|
|
38
|
-
def _get_group_input(
|
39
|
-
group_path: pathlib.Path, subgroup_prefix: str, i: int
|
40
|
-
) -> pathlib.Path:
|
41
|
-
return group_path / f'{subgroup_prefix}{i:03d}.in'
|
42
|
-
|
43
|
-
|
44
|
-
def _get_group_output(
|
45
|
-
group_path: pathlib.Path, subgroup_prefix: str, i: int
|
46
|
-
) -> pathlib.Path:
|
47
|
-
return group_path / f'{subgroup_prefix}{i:03d}.out'
|
48
|
-
|
49
|
-
|
50
|
-
def _fill_output_for_defined_testcase(testcase: Testcase) -> Testcase:
|
51
|
-
res = testcase.model_copy()
|
52
|
-
if res.outputPath is not None:
|
53
|
-
return res
|
54
|
-
output_path = res.inputPath.with_suffix('.ans')
|
55
|
-
if output_path.is_file():
|
56
|
-
res.outputPath = output_path
|
57
|
-
return res
|
58
|
-
|
59
|
-
|
60
44
|
def _copy_testcase_over(
|
61
45
|
testcase: Testcase,
|
62
46
|
dest: Testcase,
|
63
47
|
):
|
64
|
-
testcase =
|
48
|
+
testcase = fill_output_for_defined_testcase(testcase)
|
65
49
|
dest.inputPath.parent.mkdir(parents=True, exist_ok=True)
|
66
50
|
shutil.copy(
|
67
51
|
str(testcase.inputPath),
|
@@ -90,230 +74,6 @@ def get_call_from_string(call_str: str) -> GeneratorCall:
|
|
90
74
|
return GeneratorCall(name=name, args=args)
|
91
75
|
|
92
76
|
|
93
|
-
def _run_generator_script(testcase: TestcaseSubgroup) -> str:
|
94
|
-
assert testcase.generatorScript is not None
|
95
|
-
|
96
|
-
cacher = package.get_file_cacher()
|
97
|
-
|
98
|
-
if not testcase.generatorScript.path.is_file():
|
99
|
-
console.console.print(
|
100
|
-
f'[error]Generator script not found: [item]{testcase.generatorScript.path}[/item][/error]'
|
101
|
-
)
|
102
|
-
raise typer.Exit(1)
|
103
|
-
|
104
|
-
script_digest = DigestHolder()
|
105
|
-
if testcase.generatorScript.path.suffix == '.txt':
|
106
|
-
script_digest.value = cacher.put_file_from_path(testcase.generatorScript.path)
|
107
|
-
else:
|
108
|
-
try:
|
109
|
-
compiled_digest = compile_item(testcase.generatorScript)
|
110
|
-
except:
|
111
|
-
console.console.print(
|
112
|
-
f'[error]Failed compiling generator script for group [item]{testcase.name}[/item].[/error]'
|
113
|
-
)
|
114
|
-
raise
|
115
|
-
|
116
|
-
run_stderr = DigestHolder()
|
117
|
-
run_log = run_item(
|
118
|
-
testcase.generatorScript,
|
119
|
-
DigestOrSource.create(compiled_digest),
|
120
|
-
stdout=DigestOrDest.create(script_digest),
|
121
|
-
stderr=DigestOrDest.create(run_stderr),
|
122
|
-
)
|
123
|
-
|
124
|
-
if run_log is None or run_log.exitcode != 0:
|
125
|
-
console.console.print(
|
126
|
-
f'Could not run generator script for group {testcase.name}'
|
127
|
-
)
|
128
|
-
if run_log is not None:
|
129
|
-
console.console.print(
|
130
|
-
f'[error]Summary:[/error] {run_log.get_summary()}'
|
131
|
-
)
|
132
|
-
if run_stderr.value is not None:
|
133
|
-
console.console.print('[error]Stderr:[/error]')
|
134
|
-
console.console.print(
|
135
|
-
package.get_digest_as_string(run_stderr.value) or ''
|
136
|
-
)
|
137
|
-
raise typer.Exit(1)
|
138
|
-
|
139
|
-
assert script_digest.value
|
140
|
-
script = cacher.get_file_content(script_digest.value).decode()
|
141
|
-
return script
|
142
|
-
|
143
|
-
|
144
|
-
def _extract_script_lines(script: str) -> Iterable[Tuple[str, str, int]]:
|
145
|
-
lines = script.splitlines()
|
146
|
-
for i, line in enumerate(lines):
|
147
|
-
line = line.strip()
|
148
|
-
if not line:
|
149
|
-
continue
|
150
|
-
if line.startswith('#'):
|
151
|
-
continue
|
152
|
-
yield shlex.split(line)[0], shlex.join(shlex.split(line)[1:]), i + 1
|
153
|
-
|
154
|
-
|
155
|
-
class GeneratorScriptEntry(BaseModel):
|
156
|
-
path: pathlib.Path
|
157
|
-
line: int
|
158
|
-
|
159
|
-
|
160
|
-
class GenerationMetadata(BaseModel):
|
161
|
-
copied_to: Testcase
|
162
|
-
|
163
|
-
copied_from: Optional[Testcase] = None
|
164
|
-
generator_call: Optional[GeneratorCall] = None
|
165
|
-
generator_script: Optional[GeneratorScriptEntry] = None
|
166
|
-
|
167
|
-
|
168
|
-
class GenerationTestcaseEntry(BaseModel):
|
169
|
-
group_entry: TestcaseEntry
|
170
|
-
subgroup_entry: TestcaseEntry
|
171
|
-
|
172
|
-
metadata: GenerationMetadata
|
173
|
-
|
174
|
-
|
175
|
-
class TestcaseVisitor(abc.ABC):
|
176
|
-
@abc.abstractmethod
|
177
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
178
|
-
pass
|
179
|
-
|
180
|
-
def should_visit_group(self, group_name: str) -> bool:
|
181
|
-
return True
|
182
|
-
|
183
|
-
def should_visit_subgroup(self, subgroup_path: str) -> bool:
|
184
|
-
return True
|
185
|
-
|
186
|
-
def should_visit_generator_scripts(
|
187
|
-
self, group_name: str, subgroup_path: str
|
188
|
-
) -> bool:
|
189
|
-
return True
|
190
|
-
|
191
|
-
|
192
|
-
class TestcaseGroupVisitor(TestcaseVisitor):
|
193
|
-
def __init__(self, groups: Optional[Set[str]] = None):
|
194
|
-
self.groups = groups
|
195
|
-
|
196
|
-
def should_visit_group(self, group_name: str) -> bool:
|
197
|
-
return self.groups is None or group_name in self.groups
|
198
|
-
|
199
|
-
|
200
|
-
def run_testcase_visitor(visitor: TestcaseVisitor):
|
201
|
-
pkg = package.find_problem_package_or_die()
|
202
|
-
|
203
|
-
def _explore_subgroup(
|
204
|
-
subgroup: TestcaseSubgroup, subgroup_index: Optional[int], prefix: List[str]
|
205
|
-
):
|
206
|
-
assert prefix and len(prefix) >= 1 and len(prefix) <= 2
|
207
|
-
group_path = prefix[0]
|
208
|
-
subgroup_path = '/'.join(prefix)
|
209
|
-
if not visitor.should_visit_subgroup(subgroup_path):
|
210
|
-
return
|
211
|
-
|
212
|
-
def _entry(i: int) -> TestcaseEntry:
|
213
|
-
return TestcaseEntry(group=group_path, index=i)
|
214
|
-
|
215
|
-
def _sub_entry(i: int) -> TestcaseEntry:
|
216
|
-
return TestcaseEntry(group=subgroup_path, index=i)
|
217
|
-
|
218
|
-
def _copied_to(i: int) -> Testcase:
|
219
|
-
group_fs_path = package.get_build_testgroup_path(group_path)
|
220
|
-
group_prefix = ''
|
221
|
-
if subgroup_index is not None:
|
222
|
-
group_prefix = f'{subgroup_index}-'
|
223
|
-
if len(prefix) == 2:
|
224
|
-
group_prefix += f'{prefix[1]}-'
|
225
|
-
return Testcase(
|
226
|
-
inputPath=_get_group_input(group_fs_path, group_prefix, i),
|
227
|
-
outputPath=_get_group_output(group_fs_path, group_prefix, i),
|
228
|
-
)
|
229
|
-
|
230
|
-
# Go through testcases.
|
231
|
-
i = 0
|
232
|
-
# Individual testcases.
|
233
|
-
for tc in subgroup.testcases or []:
|
234
|
-
visitor.visit(
|
235
|
-
GenerationTestcaseEntry(
|
236
|
-
group_entry=_entry(i),
|
237
|
-
subgroup_entry=_sub_entry(i),
|
238
|
-
metadata=GenerationMetadata(
|
239
|
-
copied_from=_fill_output_for_defined_testcase(tc),
|
240
|
-
copied_to=_copied_to(i),
|
241
|
-
),
|
242
|
-
)
|
243
|
-
)
|
244
|
-
i += 1
|
245
|
-
|
246
|
-
# Glob testcases.
|
247
|
-
if subgroup.testcaseGlob:
|
248
|
-
matched_inputs = sorted(PosixPath().glob(subgroup.testcaseGlob))
|
249
|
-
|
250
|
-
for input_path in matched_inputs:
|
251
|
-
if not input_path.is_file() or input_path.suffix != '.in':
|
252
|
-
continue
|
253
|
-
|
254
|
-
tc = Testcase(inputPath=input_path)
|
255
|
-
visitor.visit(
|
256
|
-
GenerationTestcaseEntry(
|
257
|
-
group_entry=_entry(i),
|
258
|
-
subgroup_entry=_sub_entry(i),
|
259
|
-
metadata=GenerationMetadata(
|
260
|
-
copied_from=_fill_output_for_defined_testcase(tc),
|
261
|
-
copied_to=_copied_to(i),
|
262
|
-
),
|
263
|
-
)
|
264
|
-
)
|
265
|
-
i += 1
|
266
|
-
|
267
|
-
# Single generators.
|
268
|
-
for generator_call in subgroup.generators:
|
269
|
-
visitor.visit(
|
270
|
-
GenerationTestcaseEntry(
|
271
|
-
group_entry=_entry(i),
|
272
|
-
subgroup_entry=_sub_entry(i),
|
273
|
-
metadata=GenerationMetadata(
|
274
|
-
generator_call=generator_call,
|
275
|
-
copied_to=_copied_to(i),
|
276
|
-
),
|
277
|
-
)
|
278
|
-
)
|
279
|
-
i += 1
|
280
|
-
|
281
|
-
if not visitor.should_visit_generator_scripts(group_path, subgroup_path):
|
282
|
-
return
|
283
|
-
|
284
|
-
# Run generator script.
|
285
|
-
if subgroup.generatorScript is not None:
|
286
|
-
script = _run_generator_script(subgroup)
|
287
|
-
|
288
|
-
# Run each line from generator script.
|
289
|
-
for generator_name, args, line_number in _extract_script_lines(script):
|
290
|
-
call = GeneratorCall(name=generator_name, args=args)
|
291
|
-
visitor.visit(
|
292
|
-
GenerationTestcaseEntry(
|
293
|
-
group_entry=_entry(i),
|
294
|
-
subgroup_entry=_sub_entry(i),
|
295
|
-
metadata=GenerationMetadata(
|
296
|
-
generator_call=call,
|
297
|
-
generator_script=GeneratorScriptEntry(
|
298
|
-
path=subgroup.generatorScript.path,
|
299
|
-
line=line_number,
|
300
|
-
),
|
301
|
-
copied_to=_copied_to(i),
|
302
|
-
),
|
303
|
-
)
|
304
|
-
)
|
305
|
-
i += 1
|
306
|
-
|
307
|
-
for group in pkg.testcases:
|
308
|
-
if not visitor.should_visit_group(group.name):
|
309
|
-
continue
|
310
|
-
|
311
|
-
_explore_subgroup(group, 0 if group.subgroups else None, [group.name])
|
312
|
-
|
313
|
-
for i, subgroup in enumerate(group.subgroups):
|
314
|
-
_explore_subgroup(subgroup, i + 1, [group.name, subgroup.name])
|
315
|
-
|
316
|
-
|
317
77
|
def _get_necessary_generators_for_groups(
|
318
78
|
groups: Optional[Set[str]] = None,
|
319
79
|
) -> Set[str]:
|
@@ -439,14 +199,14 @@ def generate_standalone(
|
|
439
199
|
_, validator_digest = validator_tp
|
440
200
|
if progress:
|
441
201
|
progress.update('Validating test...')
|
442
|
-
|
202
|
+
validation_info = validators.validate_one_off(
|
443
203
|
spec.copied_to.inputPath,
|
444
204
|
validator,
|
445
205
|
validator_digest,
|
446
206
|
)
|
447
|
-
if not ok:
|
207
|
+
if not validation_info.ok:
|
448
208
|
_print_error_header('failed validating testcase.')
|
449
|
-
console.console.print(f'[error]Message:[/error] {message}')
|
209
|
+
console.console.print(f'[error]Message:[/error] {validation_info.message}')
|
450
210
|
console.console.print(
|
451
211
|
f'Testcase written at [item]{spec.copied_to.inputPath}[/item]'
|
452
212
|
)
|
@@ -552,67 +312,6 @@ def generate_output_for_testcase(
|
|
552
312
|
raise typer.Exit(1)
|
553
313
|
|
554
314
|
|
555
|
-
def extract_generation_testcases(
|
556
|
-
entries: List[TestcaseEntry],
|
557
|
-
) -> List[GenerationTestcaseEntry]:
|
558
|
-
# TODO: support subgroups.
|
559
|
-
groups = set(entry.group for entry in entries)
|
560
|
-
entry_keys = set(entry.key() for entry in entries)
|
561
|
-
|
562
|
-
res: List[GenerationTestcaseEntry] = []
|
563
|
-
|
564
|
-
class ExtractGenerationTestcasesVisitor(TestcaseVisitor):
|
565
|
-
def should_visit_group(self, group_name: str) -> bool:
|
566
|
-
return group_name in groups
|
567
|
-
|
568
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
569
|
-
# TODO: support subgroups.
|
570
|
-
if entry.group_entry.key() not in entry_keys:
|
571
|
-
return
|
572
|
-
res.append(entry)
|
573
|
-
|
574
|
-
run_testcase_visitor(ExtractGenerationTestcasesVisitor())
|
575
|
-
return res
|
576
|
-
|
577
|
-
|
578
|
-
def extract_generation_testcases_from_groups(
|
579
|
-
groups: Optional[Set[str]] = None,
|
580
|
-
) -> List[GenerationTestcaseEntry]:
|
581
|
-
res: List[GenerationTestcaseEntry] = []
|
582
|
-
|
583
|
-
class ExtractGenerationTestcasesVisitor(TestcaseGroupVisitor):
|
584
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
585
|
-
res.append(entry)
|
586
|
-
|
587
|
-
run_testcase_visitor(ExtractGenerationTestcasesVisitor(groups))
|
588
|
-
return res
|
589
|
-
|
590
|
-
|
591
|
-
def extract_generation_testcases_from_patterns(
|
592
|
-
patterns: List[TestcasePattern],
|
593
|
-
) -> List[GenerationTestcaseEntry]:
|
594
|
-
res: List[GenerationTestcaseEntry] = []
|
595
|
-
|
596
|
-
class ExtractGenerationTestcasesVisitor(TestcaseVisitor):
|
597
|
-
def should_visit_group(self, group_name: str) -> bool:
|
598
|
-
return any(pattern.intersecting_group(group_name) for pattern in patterns)
|
599
|
-
|
600
|
-
def should_visit_subgroup(self, subgroup_path: str) -> bool:
|
601
|
-
return any(
|
602
|
-
pattern.intersecting_group(subgroup_path) for pattern in patterns
|
603
|
-
)
|
604
|
-
|
605
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
606
|
-
if not any(
|
607
|
-
pattern.match(entry.group_entry) for pattern in patterns
|
608
|
-
) and not any(pattern.match(entry.subgroup_entry) for pattern in patterns):
|
609
|
-
return
|
610
|
-
res.append(entry)
|
611
|
-
|
612
|
-
run_testcase_visitor(ExtractGenerationTestcasesVisitor())
|
613
|
-
return res
|
614
|
-
|
615
|
-
|
616
315
|
def generate_outputs_for_testcases(
|
617
316
|
entries: List[TestcaseEntry],
|
618
317
|
progress: Optional[StatusProgress] = None,
|
@@ -4,10 +4,10 @@ import pytest
|
|
4
4
|
|
5
5
|
from rbx.box import package
|
6
6
|
from rbx.box.generators import (
|
7
|
-
extract_generation_testcases_from_groups,
|
8
7
|
generate_outputs_for_testcases,
|
9
8
|
generate_testcases,
|
10
9
|
)
|
10
|
+
from rbx.box.testcase_extractors import extract_generation_testcases_from_groups
|
11
11
|
from rbx.testing_utils import print_directory_tree
|
12
12
|
|
13
13
|
|
@@ -37,6 +37,7 @@ from rbx.box import (
|
|
37
37
|
validators,
|
38
38
|
)
|
39
39
|
from rbx.box.contest import main as contest
|
40
|
+
from rbx.box.contest.contest_package import find_contest_yaml
|
40
41
|
from rbx.box.environment import VerificationLevel, get_environment_path
|
41
42
|
from rbx.box.packaging import main as packaging
|
42
43
|
from rbx.box.testcases import main as testcases
|
@@ -448,6 +449,15 @@ def create(
|
|
448
449
|
Optional[str], typer.Option(help='Preset to use when creating the problem.')
|
449
450
|
] = None,
|
450
451
|
):
|
452
|
+
if find_contest_yaml() is not None:
|
453
|
+
console.console.print(
|
454
|
+
'[error]Cannot [item]rbx create[/item] a problem inside a contest.[/error]'
|
455
|
+
)
|
456
|
+
console.console.print(
|
457
|
+
'[error]Instead, use [item]rbx contest add[/item] to add a problem to a contest.[/error]'
|
458
|
+
)
|
459
|
+
raise typer.Exit(1)
|
460
|
+
|
451
461
|
if preset is not None:
|
452
462
|
creation.create(name, preset=preset)
|
453
463
|
return
|
@@ -121,8 +121,8 @@ def save_package(
|
|
121
121
|
problem_yaml_path.write_text(utils.model_to_yaml(package))
|
122
122
|
|
123
123
|
|
124
|
-
def get_ruyaml() -> Tuple[ruyaml.YAML, ruyaml.Any]:
|
125
|
-
problem_yaml_path = find_problem_yaml()
|
124
|
+
def get_ruyaml(root: pathlib.Path = pathlib.Path()) -> Tuple[ruyaml.YAML, ruyaml.Any]:
|
125
|
+
problem_yaml_path = find_problem_yaml(root)
|
126
126
|
if problem_yaml_path is None:
|
127
127
|
console.console.print(
|
128
128
|
f'Problem not found in {pathlib.Path().absolute()}', style='error'
|
@@ -232,6 +232,13 @@ A generator script to call to generate testcases for this group.
|
|
232
232
|
""",
|
233
233
|
)
|
234
234
|
|
235
|
+
extraValidators: List[CodeItem] = Field(
|
236
|
+
default=[],
|
237
|
+
description="""
|
238
|
+
A list of extra validators to use to validate the testcases of this subgroup.
|
239
|
+
""",
|
240
|
+
)
|
241
|
+
|
235
242
|
@model_validator(mode='after')
|
236
243
|
def check_oneof(self) -> 'TestcaseSubgroup':
|
237
244
|
_check_oneof(
|
@@ -260,7 +267,7 @@ A list of test subgroups to define for this group.
|
|
260
267
|
default=None,
|
261
268
|
description="""
|
262
269
|
A validator to use to validate the testcases of this group.
|
263
|
-
If
|
270
|
+
If specified, will use this validator instead of the package-level validator.
|
264
271
|
Useful in cases where the constraints vary across test groups.
|
265
272
|
""",
|
266
273
|
)
|
@@ -29,7 +29,6 @@ from rbx.box.formatting import get_formatted_memory, get_formatted_time
|
|
29
29
|
from rbx.box.generators import (
|
30
30
|
GenerationMetadata,
|
31
31
|
expand_generator_call,
|
32
|
-
extract_generation_testcases,
|
33
32
|
generate_output_for_testcase,
|
34
33
|
generate_standalone,
|
35
34
|
)
|
@@ -42,6 +41,7 @@ from rbx.box.schema import (
|
|
42
41
|
Testcase,
|
43
42
|
TestcaseGroup,
|
44
43
|
)
|
44
|
+
from rbx.box.testcase_extractors import extract_generation_testcases
|
45
45
|
from rbx.box.testcase_utils import TestcaseEntry, find_built_testcases
|
46
46
|
from rbx.grading.steps import (
|
47
47
|
DigestOrDest,
|
@@ -5,7 +5,6 @@ import pytest
|
|
5
5
|
|
6
6
|
from rbx.box.environment import VerificationLevel
|
7
7
|
from rbx.box.generators import (
|
8
|
-
extract_generation_testcases_from_groups,
|
9
8
|
generate_outputs_for_testcases,
|
10
9
|
generate_testcases,
|
11
10
|
)
|
@@ -13,6 +12,7 @@ from rbx.box.solutions import (
|
|
13
12
|
convert_list_of_solution_evaluations_to_dict,
|
14
13
|
run_solutions,
|
15
14
|
)
|
15
|
+
from rbx.box.testcase_extractors import extract_generation_testcases_from_groups
|
16
16
|
from rbx.grading.steps import Outcome
|
17
17
|
|
18
18
|
|