rbx.cp 0.5.33__tar.gz → 0.5.35__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.33 → rbx_cp-0.5.35}/PKG-INFO +1 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/pyproject.toml +1 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/builder.py +10 -2
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/code.py +1 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/build_contest_statements.py +4 -3
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/generators.py +102 -52
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/generators_test.py +5 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/main.py +23 -16
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/package.py +7 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/schema.py +3 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/solutions.py +1 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/solutions_test.py +5 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/build_statements.py +5 -2
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/builders.py +24 -5
- rbx_cp-0.5.35/rbx/box/testcase_utils.py +135 -0
- rbx_cp-0.5.35/rbx/box/testcases/main.py +158 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/validators.py +1 -1
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/config.py +15 -1
- rbx_cp-0.5.35/rbx/testdata/compatible +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/utils.py +8 -0
- rbx_cp-0.5.33/rbx/box/testcases.py +0 -70
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/LICENSE +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/README.md +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/cd.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/checkers.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/compile.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/contest_package.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/contest_utils.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/main.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/contest/statements.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/creation.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/deferred.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/extensions.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/formatting.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/contest_main.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/main.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/retries.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/sanitizers/warning_stack.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/setter_config.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/state.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/stresses.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.33/rbx/box/ui → rbx_cp-0.5.35/rbx/box/testcases}/__init__.py +0 -0
- {rbx_cp-0.5.33/rbx/grading → rbx_cp-0.5.35/rbx/box/ui}/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/checker.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/clone.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/console.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/create.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/edit.py +0 -0
- {rbx_cp-0.5.33/rbx/grading/judge → rbx_cp-0.5.35/rbx/grading}/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/caching.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.33/rbx/grading/judge/sandboxes → rbx_cp-0.5.35/rbx/grading/judge}/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/sandbox.py +0 -0
- /rbx_cp-0.5.33/rbx/testdata/compatible → /rbx_cp-0.5.35/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/sandboxes/timeit.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/steps.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/steps_with_caching.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/main.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/default_setter_config.mac.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/default_setter_config.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compare +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/run.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/schema.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/submit.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/test.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/gen1.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/gen2.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/genScript.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/ole.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/problem.rbx.yml +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/re.sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/tests/1.in +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/tle.sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/validator.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/box1/wa.sol.cpp +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testdata/caching/executable.py +0 -0
- {rbx_cp-0.5.33 → rbx_cp-0.5.35}/rbx/testing_utils.py +0 -0
@@ -3,7 +3,11 @@ from typing import Optional, Set
|
|
3
3
|
from rbx import console, utils
|
4
4
|
from rbx.box import environment, package
|
5
5
|
from rbx.box.environment import VerificationLevel
|
6
|
-
from rbx.box.generators import
|
6
|
+
from rbx.box.generators import (
|
7
|
+
extract_generation_testcases_from_groups,
|
8
|
+
generate_outputs_for_testcases,
|
9
|
+
generate_testcases,
|
10
|
+
)
|
7
11
|
from rbx.box.solutions import (
|
8
12
|
is_fast,
|
9
13
|
print_run_report,
|
@@ -61,7 +65,11 @@ def build(
|
|
61
65
|
keep=True,
|
62
66
|
) as s:
|
63
67
|
if output:
|
64
|
-
|
68
|
+
entries = [
|
69
|
+
entry.group_entry
|
70
|
+
for entry in extract_generation_testcases_from_groups(groups)
|
71
|
+
]
|
72
|
+
generate_outputs_for_testcases(entries, s)
|
65
73
|
|
66
74
|
console.console.print(
|
67
75
|
'[success]Problem built.[/success] '
|
@@ -22,6 +22,7 @@ from rbx.box.statements.builders import (
|
|
22
22
|
StatementBuilderContest,
|
23
23
|
StatementBuilderContext,
|
24
24
|
StatementBuilderProblem,
|
25
|
+
StatementSample,
|
25
26
|
prepare_assets,
|
26
27
|
)
|
27
28
|
from rbx.box.statements.joiners import (
|
@@ -30,7 +31,7 @@ from rbx.box.statements.joiners import (
|
|
30
31
|
StatementJoinerContext,
|
31
32
|
)
|
32
33
|
from rbx.box.statements.schema import Statement, StatementType
|
33
|
-
from rbx.box.
|
34
|
+
from rbx.box.testcase_utils import get_samples
|
34
35
|
|
35
36
|
|
36
37
|
@dataclasses.dataclass
|
@@ -51,7 +52,7 @@ class ExtractedProblem:
|
|
51
52
|
return StatementBuilderProblem(
|
52
53
|
package=self.package,
|
53
54
|
statement=self.statement,
|
54
|
-
samples=self.samples,
|
55
|
+
samples=StatementSample.from_testcases(self.samples),
|
55
56
|
io_path=self.built_statement,
|
56
57
|
short_name=self.problem.short_name,
|
57
58
|
)
|
@@ -124,7 +125,7 @@ def get_builder_problems(
|
|
124
125
|
StatementBuilderProblem(
|
125
126
|
package=ex.package,
|
126
127
|
statement=ex.statement,
|
127
|
-
samples=ex.samples,
|
128
|
+
samples=StatementSample.from_testcases(ex.samples),
|
128
129
|
)
|
129
130
|
for ex in extracted_problems
|
130
131
|
]
|
@@ -3,13 +3,13 @@ import pathlib
|
|
3
3
|
import shlex
|
4
4
|
import shutil
|
5
5
|
from pathlib import PosixPath
|
6
|
-
from typing import Dict, List, Optional, Set
|
6
|
+
from typing import Dict, Iterable, List, Optional, Set, Tuple
|
7
7
|
|
8
8
|
import typer
|
9
9
|
from pydantic import BaseModel
|
10
10
|
|
11
11
|
from rbx import console
|
12
|
-
from rbx.box import checkers, package,
|
12
|
+
from rbx.box import checkers, package, testcase_utils, validators
|
13
13
|
from rbx.box.code import SanitizationLevel, compile_item, run_item
|
14
14
|
from rbx.box.environment import (
|
15
15
|
EnvironmentSandbox,
|
@@ -22,7 +22,7 @@ from rbx.box.schema import (
|
|
22
22
|
TestcaseSubgroup,
|
23
23
|
)
|
24
24
|
from rbx.box.stressing import generator_parser
|
25
|
-
from rbx.box.
|
25
|
+
from rbx.box.testcase_utils import TestcaseEntry, TestcasePattern, find_built_testcases
|
26
26
|
from rbx.grading.steps import (
|
27
27
|
DigestHolder,
|
28
28
|
DigestOrDest,
|
@@ -141,15 +141,20 @@ def _run_generator_script(testcase: TestcaseSubgroup) -> str:
|
|
141
141
|
return script
|
142
142
|
|
143
143
|
|
144
|
-
def _extract_script_lines(script: str):
|
144
|
+
def _extract_script_lines(script: str) -> Iterable[Tuple[str, str, int]]:
|
145
145
|
lines = script.splitlines()
|
146
|
-
for line in lines:
|
146
|
+
for i, line in enumerate(lines):
|
147
147
|
line = line.strip()
|
148
148
|
if not line:
|
149
149
|
continue
|
150
150
|
if line.startswith('#'):
|
151
151
|
continue
|
152
|
-
yield shlex.split(line)[0], shlex.join(shlex.split(line)[1:])
|
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
|
153
158
|
|
154
159
|
|
155
160
|
class GenerationMetadata(BaseModel):
|
@@ -157,6 +162,7 @@ class GenerationMetadata(BaseModel):
|
|
157
162
|
|
158
163
|
copied_from: Optional[Testcase] = None
|
159
164
|
generator_call: Optional[GeneratorCall] = None
|
165
|
+
generator_script: Optional[GeneratorScriptEntry] = None
|
160
166
|
|
161
167
|
|
162
168
|
class GenerationTestcaseEntry(BaseModel):
|
@@ -280,7 +286,7 @@ def run_testcase_visitor(visitor: TestcaseVisitor):
|
|
280
286
|
script = _run_generator_script(subgroup)
|
281
287
|
|
282
288
|
# Run each line from generator script.
|
283
|
-
for generator_name, args in _extract_script_lines(script):
|
289
|
+
for generator_name, args, line_number in _extract_script_lines(script):
|
284
290
|
call = GeneratorCall(name=generator_name, args=args)
|
285
291
|
visitor.visit(
|
286
292
|
GenerationTestcaseEntry(
|
@@ -288,6 +294,10 @@ def run_testcase_visitor(visitor: TestcaseVisitor):
|
|
288
294
|
subgroup_entry=_sub_entry(i),
|
289
295
|
metadata=GenerationMetadata(
|
290
296
|
generator_call=call,
|
297
|
+
generator_script=GeneratorScriptEntry(
|
298
|
+
path=subgroup.generatorScript.path,
|
299
|
+
line=line_number,
|
300
|
+
),
|
291
301
|
copied_to=_copied_to(i),
|
292
302
|
),
|
293
303
|
)
|
@@ -457,7 +467,7 @@ def generate_testcases(
|
|
457
467
|
else None,
|
458
468
|
)
|
459
469
|
|
460
|
-
|
470
|
+
testcase_utils.clear_built_testcases()
|
461
471
|
|
462
472
|
class BuildTestcaseVisitor(TestcaseGroupVisitor):
|
463
473
|
def visit(self, entry: GenerationTestcaseEntry):
|
@@ -487,6 +497,8 @@ def generate_output_for_testcase(
|
|
487
497
|
stderr_path: Optional[pathlib.Path] = None,
|
488
498
|
):
|
489
499
|
assert testcase.outputPath is not None
|
500
|
+
testcase.inputPath.parent.mkdir(parents=True, exist_ok=True)
|
501
|
+
testcase.outputPath.parent.mkdir(parents=True, exist_ok=True)
|
490
502
|
|
491
503
|
if testcase.outputPath.is_file():
|
492
504
|
# Output file was already copied over from manual tests.
|
@@ -540,8 +552,70 @@ def generate_output_for_testcase(
|
|
540
552
|
raise typer.Exit(1)
|
541
553
|
|
542
554
|
|
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
|
+
|
543
616
|
def generate_outputs_for_testcases(
|
544
|
-
|
617
|
+
entries: List[TestcaseEntry],
|
618
|
+
progress: Optional[StatusProgress] = None,
|
545
619
|
):
|
546
620
|
def step():
|
547
621
|
if progress is not None:
|
@@ -563,50 +637,26 @@ def generate_outputs_for_testcases(
|
|
563
637
|
shutil.rmtree(str(gen_runs_dir), ignore_errors=True)
|
564
638
|
gen_runs_dir.mkdir(parents=True, exist_ok=True)
|
565
639
|
|
566
|
-
|
567
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
568
|
-
tc = entry.metadata.copied_to
|
569
|
-
if not tc.inputPath.is_file():
|
570
|
-
return
|
571
|
-
assert tc.outputPath is not None
|
640
|
+
generation_entries = extract_generation_testcases(entries)
|
572
641
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
)
|
579
|
-
raise typer.Exit(1)
|
642
|
+
for entry in generation_entries:
|
643
|
+
tc = entry.metadata.copied_to
|
644
|
+
if not tc.inputPath.is_file():
|
645
|
+
return
|
646
|
+
assert tc.outputPath is not None
|
580
647
|
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
648
|
+
if (
|
649
|
+
main_solution is None or solution_digest is None
|
650
|
+
) and not tc.outputPath.is_file():
|
651
|
+
console.console.print(
|
652
|
+
'[error]No main solution found to generate outputs for testcases.[/error]',
|
586
653
|
)
|
587
|
-
|
588
|
-
|
589
|
-
run_testcase_visitor(GenerateOutputsVisitor(groups))
|
590
|
-
|
591
|
-
|
592
|
-
def extract_generation_testcases(
|
593
|
-
entries: List[TestcaseEntry],
|
594
|
-
) -> List[GenerationTestcaseEntry]:
|
595
|
-
# TODO: support subgroups.
|
596
|
-
groups = set(entry.group for entry in entries)
|
597
|
-
entry_keys = set(entry.key() for entry in entries)
|
598
|
-
|
599
|
-
res: List[GenerationTestcaseEntry] = []
|
600
|
-
|
601
|
-
class ExtractGenerationTestcasesVisitor(TestcaseVisitor):
|
602
|
-
def should_visit_group(self, group_name: str) -> bool:
|
603
|
-
return group_name in groups
|
604
|
-
|
605
|
-
def visit(self, entry: GenerationTestcaseEntry):
|
606
|
-
# TODO: support subgroups.
|
607
|
-
if entry.group_entry.key() not in entry_keys:
|
608
|
-
return
|
609
|
-
res.append(entry)
|
654
|
+
raise typer.Exit(1)
|
610
655
|
|
611
|
-
|
612
|
-
|
656
|
+
assert solution_digest is not None
|
657
|
+
generate_output_for_testcase(
|
658
|
+
solution_digest,
|
659
|
+
tc,
|
660
|
+
gen_runs_dir / 'main.stderr',
|
661
|
+
)
|
662
|
+
step()
|
@@ -4,6 +4,7 @@ import pytest
|
|
4
4
|
|
5
5
|
from rbx.box import package
|
6
6
|
from rbx.box.generators import (
|
7
|
+
extract_generation_testcases_from_groups,
|
7
8
|
generate_outputs_for_testcases,
|
8
9
|
generate_testcases,
|
9
10
|
)
|
@@ -13,7 +14,10 @@ from rbx.testing_utils import print_directory_tree
|
|
13
14
|
@pytest.mark.test_pkg('box1')
|
14
15
|
def test_generator_works(pkg_from_testdata: pathlib.Path):
|
15
16
|
generate_testcases()
|
16
|
-
|
17
|
+
entries = [
|
18
|
+
entry.group_entry for entry in extract_generation_testcases_from_groups()
|
19
|
+
]
|
20
|
+
generate_outputs_for_testcases(entries)
|
17
21
|
|
18
22
|
# Debug when fail.
|
19
23
|
print_directory_tree(pkg_from_testdata)
|
@@ -39,6 +39,7 @@ from rbx.box import (
|
|
39
39
|
from rbx.box.contest import main as contest
|
40
40
|
from rbx.box.environment import VerificationLevel, get_environment_path
|
41
41
|
from rbx.box.packaging import main as packaging
|
42
|
+
from rbx.box.testcases import main as testcases
|
42
43
|
from rbx.box.solutions import (
|
43
44
|
estimate_time_limit,
|
44
45
|
get_exact_matching_solutions,
|
@@ -49,7 +50,7 @@ from rbx.box.solutions import (
|
|
49
50
|
run_solutions,
|
50
51
|
)
|
51
52
|
from rbx.box.statements import build_statements
|
52
|
-
from rbx.box.
|
53
|
+
from rbx.box.testcase_utils import TestcaseEntry
|
53
54
|
|
54
55
|
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
55
56
|
app.add_typer(
|
@@ -82,6 +83,12 @@ app.add_typer(
|
|
82
83
|
app.add_typer(
|
83
84
|
contest.app, name='contest', cls=annotations.AliasGroup, help='Contest management.'
|
84
85
|
)
|
86
|
+
app.add_typer(
|
87
|
+
testcases.app,
|
88
|
+
name='testcases, tc, t',
|
89
|
+
cls=annotations.AliasGroup,
|
90
|
+
help='Testcase management.',
|
91
|
+
)
|
85
92
|
|
86
93
|
|
87
94
|
@app.callback()
|
@@ -166,6 +173,21 @@ def run(
|
|
166
173
|
)
|
167
174
|
check = False
|
168
175
|
|
176
|
+
tracked_solutions = None
|
177
|
+
if outcome is not None:
|
178
|
+
tracked_solutions = {
|
179
|
+
str(solution.path)
|
180
|
+
for solution in get_matching_solutions(ExpectedOutcome(outcome))
|
181
|
+
}
|
182
|
+
if solution:
|
183
|
+
tracked_solutions = {solution}
|
184
|
+
|
185
|
+
if choice:
|
186
|
+
tracked_solutions = set(pick_solutions(tracked_solutions))
|
187
|
+
if not tracked_solutions:
|
188
|
+
console.console.print('[error]No solutions selected. Exiting.[/error]')
|
189
|
+
raise typer.Exit(1)
|
190
|
+
|
169
191
|
if not builder.build(verification=verification, output=check):
|
170
192
|
return
|
171
193
|
|
@@ -195,21 +217,6 @@ def run(
|
|
195
217
|
'and the environment default time limit will be used instead.[/warning]'
|
196
218
|
)
|
197
219
|
|
198
|
-
tracked_solutions = None
|
199
|
-
if outcome is not None:
|
200
|
-
tracked_solutions = {
|
201
|
-
str(solution.path)
|
202
|
-
for solution in get_matching_solutions(ExpectedOutcome(outcome))
|
203
|
-
}
|
204
|
-
if solution:
|
205
|
-
tracked_solutions = {solution}
|
206
|
-
|
207
|
-
if choice:
|
208
|
-
tracked_solutions = set(pick_solutions(tracked_solutions))
|
209
|
-
if not tracked_solutions:
|
210
|
-
console.console.print('[error]No solutions selected. Exiting.[/error]')
|
211
|
-
raise typer.Exit(1)
|
212
|
-
|
213
220
|
if sanitized and tracked_solutions is None:
|
214
221
|
console.console.print(
|
215
222
|
'[warning]Sanitizers are running, and no solutions were specified to run. Will only run [item]ACCEPTED[/item] solutions.'
|
@@ -364,6 +364,13 @@ def get_compilation_files(code: CodeItem) -> List[Tuple[pathlib.Path, pathlib.Pa
|
|
364
364
|
return res
|
365
365
|
|
366
366
|
|
367
|
+
@functools.cache
|
368
|
+
def get_empty_sentinel_path(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
369
|
+
path = get_problem_cache_dir(root) / '.empty'
|
370
|
+
path.write_text('')
|
371
|
+
return path
|
372
|
+
|
373
|
+
|
367
374
|
def clear_package_cache():
|
368
375
|
pkgs = [sys.modules[__name__]]
|
369
376
|
|
@@ -192,6 +192,9 @@ class GeneratorCall(BaseModel):
|
|
192
192
|
default=None, description='The arguments to pass to the generator.'
|
193
193
|
)
|
194
194
|
|
195
|
+
def __str__(self) -> str:
|
196
|
+
return f'{self.name} {self.args}'
|
197
|
+
|
195
198
|
|
196
199
|
class TestcaseSubgroup(BaseModel):
|
197
200
|
model_config = ConfigDict(extra='forbid')
|
@@ -42,7 +42,7 @@ from rbx.box.schema import (
|
|
42
42
|
Testcase,
|
43
43
|
TestcaseGroup,
|
44
44
|
)
|
45
|
-
from rbx.box.
|
45
|
+
from rbx.box.testcase_utils import TestcaseEntry, find_built_testcases
|
46
46
|
from rbx.grading.steps import (
|
47
47
|
DigestOrDest,
|
48
48
|
DigestOrSource,
|
@@ -5,6 +5,7 @@ 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,
|
8
9
|
generate_outputs_for_testcases,
|
9
10
|
generate_testcases,
|
10
11
|
)
|
@@ -18,7 +19,10 @@ from rbx.grading.steps import Outcome
|
|
18
19
|
@pytest.mark.test_pkg('box1')
|
19
20
|
def test_solutions(pkg_from_testdata: pathlib.Path):
|
20
21
|
generate_testcases()
|
21
|
-
|
22
|
+
entries = [
|
23
|
+
entry.group_entry for entry in extract_generation_testcases_from_groups()
|
24
|
+
]
|
25
|
+
generate_outputs_for_testcases(entries)
|
22
26
|
|
23
27
|
result = run_solutions(verification=VerificationLevel.FULL)
|
24
28
|
res = asyncio.run(convert_list_of_solution_evaluations_to_dict(result.items))
|
@@ -15,6 +15,7 @@ from rbx.box.statements.builders import (
|
|
15
15
|
StatementBuilderContext,
|
16
16
|
StatementBuilderProblem,
|
17
17
|
StatementCodeLanguage,
|
18
|
+
StatementSample,
|
18
19
|
prepare_assets,
|
19
20
|
)
|
20
21
|
from rbx.box.statements.schema import (
|
@@ -23,7 +24,7 @@ from rbx.box.statements.schema import (
|
|
23
24
|
Statement,
|
24
25
|
StatementType,
|
25
26
|
)
|
26
|
-
from rbx.box.
|
27
|
+
from rbx.box.testcase_utils import get_samples
|
27
28
|
|
28
29
|
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
29
30
|
|
@@ -263,7 +264,9 @@ def build_statement_bytes(
|
|
263
264
|
item=StatementBuilderProblem(
|
264
265
|
package=pkg,
|
265
266
|
statement=statement,
|
266
|
-
samples=
|
267
|
+
samples=StatementSample.from_testcases(
|
268
|
+
get_samples() if use_samples else []
|
269
|
+
),
|
267
270
|
short_name=short_name,
|
268
271
|
),
|
269
272
|
verbose=False,
|
@@ -7,8 +7,9 @@ from abc import ABC, abstractmethod
|
|
7
7
|
from typing import Any, Dict, List, Optional, Tuple
|
8
8
|
|
9
9
|
import typer
|
10
|
+
from pydantic import BaseModel
|
10
11
|
|
11
|
-
from rbx import console
|
12
|
+
from rbx import console, utils
|
12
13
|
from rbx.box.schema import Package, Primitive, Testcase
|
13
14
|
from rbx.box.statements.latex import (
|
14
15
|
MAX_PDFLATEX_RUNS,
|
@@ -64,7 +65,25 @@ class StatementBuilderItem(ABC):
|
|
64
65
|
pass
|
65
66
|
|
66
67
|
|
67
|
-
class StatementSample(
|
68
|
+
class StatementSample(BaseModel):
|
69
|
+
inputPath: pathlib.Path
|
70
|
+
outputPath: pathlib.Path
|
71
|
+
hasOutput: bool = True
|
72
|
+
|
73
|
+
@staticmethod
|
74
|
+
def from_testcase(testcase: Testcase) -> 'StatementSample':
|
75
|
+
return StatementSample(
|
76
|
+
inputPath=testcase.inputPath,
|
77
|
+
outputPath=testcase.outputPath or utils.get_empty_sentinel_path(),
|
78
|
+
hasOutput=testcase.outputPath is not None,
|
79
|
+
)
|
80
|
+
|
81
|
+
@staticmethod
|
82
|
+
def from_testcases(testcases: List[Testcase]) -> List['StatementSample']:
|
83
|
+
return [StatementSample.from_testcase(testcase) for testcase in testcases]
|
84
|
+
|
85
|
+
|
86
|
+
class ExplainedStatementSample(StatementSample):
|
68
87
|
explanation: Optional[str] = None
|
69
88
|
|
70
89
|
|
@@ -72,7 +91,7 @@ class StatementSample(Testcase):
|
|
72
91
|
class StatementBuilderProblem(StatementBuilderItem):
|
73
92
|
package: Package
|
74
93
|
statement: Statement
|
75
|
-
samples: List[
|
94
|
+
samples: List[StatementSample] = dataclasses.field(default_factory=list)
|
76
95
|
short_name: Optional[str] = None
|
77
96
|
|
78
97
|
# Will only be filled by contests.
|
@@ -301,8 +320,8 @@ class rbxTeXBuilder(StatementBuilder):
|
|
301
320
|
problem_kwargs['problem']['blocks'] = blocks
|
302
321
|
if statement_blocks.explanations is not None:
|
303
322
|
problem_kwargs['problem']['samples'] = [
|
304
|
-
|
305
|
-
**typing.cast(
|
323
|
+
ExplainedStatementSample(
|
324
|
+
**typing.cast(StatementSample, sample).model_dump(),
|
306
325
|
explanation=statement_blocks.explanations.get(i),
|
307
326
|
)
|
308
327
|
for i, sample in enumerate(problem_kwargs['problem']['samples'])
|