rbx.cp 0.5.16__tar.gz → 0.5.18__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.16 → rbx_cp-0.5.18}/PKG-INFO +2 -1
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/pyproject.toml +2 -1
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/builder.py +2 -2
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/cd.py +12 -2
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/checkers.py +38 -4
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/code.py +128 -18
- rbx_cp-0.5.18/rbx/box/compile.py +82 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/build_contest_statements.py +7 -6
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/contest_package.py +15 -2
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/contest_utils.py +0 -9
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/main.py +35 -24
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/statements.py +4 -5
- rbx_cp-0.5.18/rbx/box/deferred.py +26 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/extensions.py +0 -8
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/generators.py +6 -4
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/main.py +195 -19
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/package.py +24 -2
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/contest_main.py +5 -5
- rbx_cp-0.5.18/rbx/box/sanitizers/warning_stack.py +90 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/schema.py +15 -1
- rbx_cp-0.5.18/rbx/box/setter_config.py +132 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/solutions.py +237 -149
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/solutions_test.py +2 -1
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/stresses.py +5 -3
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/validators.py +5 -5
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/config.py +3 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/caching.py +4 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/sandboxes/stupid_sandbox.py +2 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/sandboxes/timeit.py +3 -3
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/steps.py +143 -15
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/steps_with_caching.py +2 -0
- rbx_cp-0.5.18/rbx/resources/default_setter_config.mac.yml +31 -0
- rbx_cp-0.5.18/rbx/resources/default_setter_config.yml +29 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/utils.py +8 -1
- rbx_cp-0.5.16/rbx/box/compile.py +0 -50
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/LICENSE +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/README.md +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/creation.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/generators_test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/main.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/build_statements.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/builders.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/testcases.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/ui/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/checker.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/clone.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/console.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/create.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/edit.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/sandbox.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/main.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compare +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/run.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/schema.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/submit.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/test.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/gen1.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/gen2.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/genScript.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/ole.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/problem.rbx.yml +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/re.sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/tests/1.in +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/tle.sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/validator.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/box1/wa.sol.cpp +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/caching/executable.py +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testdata/compatible +0 -0
- {rbx_cp-0.5.16 → rbx_cp-0.5.18}/rbx/testing_utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: rbx.cp
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.18
|
4
4
|
Summary:
|
5
5
|
Author: Roberto Sales
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -27,6 +27,7 @@ Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
27
27
|
Requires-Dist: questionary (>=2.1.0,<3.0.0)
|
28
28
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
29
29
|
Requires-Dist: rich (>=13.9.4,<14.0.0)
|
30
|
+
Requires-Dist: ruyaml (>=0.91.0,<0.92.0)
|
30
31
|
Requires-Dist: textual (>=0.79.1,<0.80.0)
|
31
32
|
Requires-Dist: typer (>=0.15.1,<0.16.0)
|
32
33
|
Description-Content-Type: text/markdown
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "rbx.cp"
|
3
|
-
version = "0.5.
|
3
|
+
version = "0.5.18"
|
4
4
|
description = ""
|
5
5
|
packages = [
|
6
6
|
{include = "rbx"}
|
@@ -30,6 +30,7 @@ pyte = "^0.8.2"
|
|
30
30
|
questionary = "^2.1.0"
|
31
31
|
lark = "^1.2.2"
|
32
32
|
chardet = "^5.2.0"
|
33
|
+
ruyaml = "^0.91.0"
|
33
34
|
|
34
35
|
[tool.poetry.scripts]
|
35
36
|
rbc = "rbx.main:app"
|
@@ -76,7 +76,7 @@ def build(
|
|
76
76
|
return True
|
77
77
|
|
78
78
|
|
79
|
-
def verify(verification: environment.VerificationParam) -> bool:
|
79
|
+
async def verify(verification: environment.VerificationParam) -> bool:
|
80
80
|
if not build(verification=verification):
|
81
81
|
return False
|
82
82
|
|
@@ -100,7 +100,7 @@ def verify(verification: environment.VerificationParam) -> bool:
|
|
100
100
|
|
101
101
|
console.console.print()
|
102
102
|
console.console.rule('[status]Run report[/status]', style='status')
|
103
|
-
return print_run_report(
|
103
|
+
return await print_run_report(
|
104
104
|
solution_result,
|
105
105
|
console.console,
|
106
106
|
verification,
|
@@ -1,10 +1,13 @@
|
|
1
|
+
import contextlib
|
1
2
|
import functools
|
2
3
|
import pathlib
|
3
4
|
from typing import Optional
|
4
5
|
|
5
6
|
import typer
|
6
7
|
|
7
|
-
from rbx import console
|
8
|
+
from rbx import console
|
9
|
+
from rbx.box.sanitizers import warning_stack
|
10
|
+
from rbx.utils import new_cd
|
8
11
|
|
9
12
|
|
10
13
|
def find_package(root: pathlib.Path = pathlib.Path()) -> Optional[pathlib.Path]:
|
@@ -30,7 +33,14 @@ def within_closest_package(func):
|
|
30
33
|
console.console.print('[error]No rbx package found.[/error]')
|
31
34
|
raise typer.Exit(1)
|
32
35
|
# Get deepest package.
|
33
|
-
with
|
36
|
+
with new_package_cd(package):
|
34
37
|
return func(*args, **kwargs)
|
35
38
|
|
36
39
|
return wrapper
|
40
|
+
|
41
|
+
|
42
|
+
@contextlib.contextmanager
|
43
|
+
def new_package_cd(x: pathlib.Path):
|
44
|
+
with new_cd(x):
|
45
|
+
yield
|
46
|
+
warning_stack.print_warning_stack_report()
|
@@ -5,7 +5,7 @@ import typer
|
|
5
5
|
|
6
6
|
from rbx import console
|
7
7
|
from rbx.box import package
|
8
|
-
from rbx.box.code import compile_item, run_item
|
8
|
+
from rbx.box.code import SanitizationLevel, compile_item, run_item
|
9
9
|
from rbx.box.schema import Testcase
|
10
10
|
from rbx.grading.judge.sandbox import SandboxBase
|
11
11
|
from rbx.grading.steps import (
|
@@ -23,7 +23,7 @@ def compile_checker() -> str:
|
|
23
23
|
checker = package.get_checker()
|
24
24
|
|
25
25
|
try:
|
26
|
-
digest = compile_item(checker)
|
26
|
+
digest = compile_item(checker, sanitized=SanitizationLevel.PREFER)
|
27
27
|
except Exception as e:
|
28
28
|
console.console.print('[error]Failed compiling checker.[/error]')
|
29
29
|
raise typer.Exit(1) from e
|
@@ -33,11 +33,21 @@ def compile_checker() -> str:
|
|
33
33
|
def _check_pre_output(run_log: Optional[RunLog]) -> CheckerResult:
|
34
34
|
pkg = package.find_problem_package_or_die()
|
35
35
|
|
36
|
+
is_sanitized = (
|
37
|
+
run_log is not None
|
38
|
+
and run_log.metadata is not None
|
39
|
+
and run_log.metadata.is_sanitized
|
40
|
+
)
|
41
|
+
|
36
42
|
if run_log is None:
|
37
43
|
return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
|
38
44
|
|
39
45
|
timelimit = pkg.timelimit_for_language(run_log.get_run_language())
|
40
|
-
if
|
46
|
+
if (
|
47
|
+
run_log.time is not None
|
48
|
+
and run_log.time * 1000 > timelimit * 2
|
49
|
+
and not is_sanitized
|
50
|
+
):
|
41
51
|
return CheckerResult(outcome=Outcome.TIME_LIMIT_EXCEEDED)
|
42
52
|
|
43
53
|
if run_log.exitstatus in [SandboxBase.EXIT_SIGNAL, SandboxBase.EXIT_NONZERO_RETURN]:
|
@@ -58,11 +68,17 @@ def _convert_tle(result: CheckerResult, run_log: Optional[RunLog]) -> CheckerRes
|
|
58
68
|
# This already is a TLE outcome.
|
59
69
|
return result
|
60
70
|
pkg = package.find_problem_package_or_die()
|
71
|
+
is_sanitized = (
|
72
|
+
run_log is not None
|
73
|
+
and run_log.metadata is not None
|
74
|
+
and run_log.metadata.is_sanitized
|
75
|
+
)
|
61
76
|
if (
|
62
77
|
run_log is not None
|
63
78
|
and run_log.time is not None
|
64
79
|
and run_log.time * 1000
|
65
80
|
>= pkg.timelimit_for_language(run_log.get_run_language())
|
81
|
+
and not is_sanitized
|
66
82
|
):
|
67
83
|
# Soft TLE.
|
68
84
|
result.no_tle_outcome = result.outcome
|
@@ -75,7 +91,7 @@ def check_with_no_output(run_log: Optional[RunLog]) -> CheckerResult:
|
|
75
91
|
return _convert_tle(result, run_log)
|
76
92
|
|
77
93
|
|
78
|
-
def
|
94
|
+
def _check(
|
79
95
|
checker_digest: str,
|
80
96
|
run_log: Optional[RunLog],
|
81
97
|
testcase: Testcase,
|
@@ -157,3 +173,21 @@ def check(
|
|
157
173
|
if skip_run_log:
|
158
174
|
return result
|
159
175
|
return _convert_tle(result, run_log)
|
176
|
+
|
177
|
+
|
178
|
+
def _check_sanitizer_warnings(run_log: Optional[RunLog]) -> bool:
|
179
|
+
if run_log is None:
|
180
|
+
return False
|
181
|
+
return run_log.warnings
|
182
|
+
|
183
|
+
|
184
|
+
def check(
|
185
|
+
checker_digest: str,
|
186
|
+
run_log: Optional[RunLog],
|
187
|
+
testcase: Testcase,
|
188
|
+
program_output: pathlib.Path,
|
189
|
+
skip_run_log: bool = False,
|
190
|
+
) -> CheckerResult:
|
191
|
+
result = _check(checker_digest, run_log, testcase, program_output, skip_run_log)
|
192
|
+
result.sanitizer_warnings = _check_sanitizer_warnings(run_log)
|
193
|
+
return result
|
@@ -1,17 +1,19 @@
|
|
1
1
|
import pathlib
|
2
2
|
import shlex
|
3
|
-
import
|
3
|
+
from enum import Enum
|
4
4
|
from pathlib import PosixPath
|
5
5
|
from typing import List, Optional
|
6
6
|
|
7
|
+
import rich
|
8
|
+
import rich.text
|
7
9
|
import typer
|
8
10
|
|
9
|
-
from rbx
|
11
|
+
from rbx import console
|
12
|
+
from rbx.box import download, package, setter_config
|
10
13
|
from rbx.box.environment import (
|
11
14
|
ExecutionConfig,
|
12
15
|
get_compilation_config,
|
13
16
|
get_execution_config,
|
14
|
-
get_extension_or_default,
|
15
17
|
get_file_mapping,
|
16
18
|
get_language,
|
17
19
|
get_mapped_command,
|
@@ -19,7 +21,7 @@ from rbx.box.environment import (
|
|
19
21
|
get_sandbox_params_from_config,
|
20
22
|
merge_execution_configs,
|
21
23
|
)
|
22
|
-
from rbx.box.
|
24
|
+
from rbx.box.sanitizers import warning_stack
|
23
25
|
from rbx.box.schema import CodeItem
|
24
26
|
from rbx.grading import steps_with_caching
|
25
27
|
from rbx.grading.steps import (
|
@@ -31,17 +33,25 @@ from rbx.grading.steps import (
|
|
31
33
|
GradingFileOutput,
|
32
34
|
RunLog,
|
33
35
|
RunLogMetadata,
|
36
|
+
is_cxx_command,
|
34
37
|
)
|
35
38
|
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return command
|
42
|
-
return command.replace('g++', extension.gpp_alternative)
|
40
|
+
class SanitizationLevel(Enum):
|
41
|
+
NONE = 0
|
42
|
+
PREFER = 1
|
43
|
+
FORCE = 2
|
43
44
|
|
44
|
-
|
45
|
+
def should_sanitize(self) -> bool:
|
46
|
+
cfg = setter_config.get_setter_config()
|
47
|
+
if cfg.sanitizers.enabled:
|
48
|
+
return self.value >= SanitizationLevel.PREFER.value
|
49
|
+
return self.value >= SanitizationLevel.FORCE.value
|
50
|
+
|
51
|
+
|
52
|
+
def substitute_commands(commands: List[str], sanitized: bool = False) -> List[str]:
|
53
|
+
cfg = setter_config.get_setter_config()
|
54
|
+
return [cfg.substitute_command(command, sanitized) for command in commands]
|
45
55
|
|
46
56
|
|
47
57
|
def get_extension(code: CodeItem) -> str:
|
@@ -55,8 +65,46 @@ def find_language_name(code: CodeItem) -> str:
|
|
55
65
|
return get_language(get_extension(code)).name
|
56
66
|
|
57
67
|
|
68
|
+
def is_executable_sanitized(executable: DigestOrSource) -> bool:
|
69
|
+
if executable.digest is None:
|
70
|
+
return False
|
71
|
+
storage = package.get_cache_storage()
|
72
|
+
return storage.exists(f'{executable.digest.value}.san')
|
73
|
+
|
74
|
+
|
75
|
+
def add_sanitizer_flags_to_command(command: str) -> str:
|
76
|
+
if is_cxx_command(command):
|
77
|
+
return command + ' -fsanitize=address,undefined -fno-omit-frame-pointer'
|
78
|
+
return command
|
79
|
+
|
80
|
+
|
81
|
+
def add_sanitizer_flags(commands: List[str]) -> List[str]:
|
82
|
+
return [add_sanitizer_flags_to_command(command) for command in commands]
|
83
|
+
|
84
|
+
|
85
|
+
def add_warning_flags_to_command(command: str) -> str:
|
86
|
+
if is_cxx_command(command):
|
87
|
+
return (
|
88
|
+
command
|
89
|
+
+ ' -Wall -Wshadow -Wno-unused-result -Wno-sign-compare -Wno-char-subscripts'
|
90
|
+
)
|
91
|
+
return command
|
92
|
+
|
93
|
+
|
94
|
+
def add_warning_flags(commands: List[str], force_warnings: bool) -> List[str]:
|
95
|
+
cfg = setter_config.get_setter_config()
|
96
|
+
if cfg.warnings.enabled or force_warnings:
|
97
|
+
return [add_warning_flags_to_command(command) for command in commands]
|
98
|
+
return commands
|
99
|
+
|
100
|
+
|
58
101
|
# Compile code item and return its digest in the storage.
|
59
|
-
def compile_item(
|
102
|
+
def compile_item(
|
103
|
+
code: CodeItem,
|
104
|
+
sanitized: SanitizationLevel = SanitizationLevel.PREFER,
|
105
|
+
force_warnings: bool = False,
|
106
|
+
verbose: bool = False,
|
107
|
+
) -> str:
|
60
108
|
generator_path = PosixPath(code.path)
|
61
109
|
language = find_language_name(code)
|
62
110
|
compilation_options = get_compilation_config(language)
|
@@ -69,10 +117,20 @@ def compile_item(code: CodeItem) -> str:
|
|
69
117
|
# Language is not compiled.
|
70
118
|
return sandbox.file_cacher.put_file_from_path(generator_path)
|
71
119
|
|
72
|
-
# Compile the generator
|
73
120
|
commands = get_mapped_commands(compilation_options.commands, file_mapping)
|
74
|
-
|
75
|
-
|
121
|
+
commands = add_warning_flags(commands, force_warnings)
|
122
|
+
commands = substitute_commands(commands, sanitized=sanitized.should_sanitize())
|
123
|
+
|
124
|
+
if sanitized.should_sanitize():
|
125
|
+
commands = add_sanitizer_flags(commands)
|
126
|
+
|
127
|
+
# Remove any memory constraints for a sanitized executable.
|
128
|
+
# Sanitizers are known to be memory-hungry.
|
129
|
+
sandbox_params.address_space = None
|
130
|
+
|
131
|
+
# Reset timeout configs since sanitizers are known to be time-hungry.
|
132
|
+
sandbox_params.timeout = None
|
133
|
+
sandbox_params.wallclock_timeout = None
|
76
134
|
|
77
135
|
compiled_digest = DigestHolder()
|
78
136
|
|
@@ -104,6 +162,33 @@ def compile_item(code: CodeItem) -> str:
|
|
104
162
|
raise typer.Exit(1)
|
105
163
|
|
106
164
|
assert compiled_digest.value is not None
|
165
|
+
|
166
|
+
if verbose and artifacts.logs is not None and artifacts.logs.preprocess is not None:
|
167
|
+
for log in artifacts.logs.preprocess:
|
168
|
+
console.console.print(f'[status]Command:[/status] {log.get_command()}')
|
169
|
+
console.console.print(f'[status]Summary:[/status] {log.get_summary()}')
|
170
|
+
console.console.print(rich.text.Text.from_ansi(log.log), style='default')
|
171
|
+
|
172
|
+
# Write compiler warnings.
|
173
|
+
cfg = setter_config.get_setter_config()
|
174
|
+
if (
|
175
|
+
(cfg.warnings.enabled or force_warnings)
|
176
|
+
and artifacts.logs is not None
|
177
|
+
and artifacts.logs.preprocess is not None
|
178
|
+
):
|
179
|
+
any_warning = any(log.warnings for log in artifacts.logs.preprocess)
|
180
|
+
if any_warning:
|
181
|
+
warning_stack.get_warning_stack().add_warning(code)
|
182
|
+
|
183
|
+
# Create sentinel to indicate this executable is sanitized.
|
184
|
+
storage = package.get_cache_storage()
|
185
|
+
if sanitized.should_sanitize():
|
186
|
+
pf = storage.create_file(f'{compiled_digest.value}.san')
|
187
|
+
if pf is not None:
|
188
|
+
storage.commit_file(pf)
|
189
|
+
elif storage.exists(f'{compiled_digest.value}.san'):
|
190
|
+
storage.delete(f'{compiled_digest.value}.san')
|
191
|
+
|
107
192
|
return compiled_digest.value
|
108
193
|
|
109
194
|
|
@@ -127,14 +212,29 @@ def run_item(
|
|
127
212
|
sandbox = package.get_singleton_sandbox()
|
128
213
|
sandbox_params = get_sandbox_params_from_config(execution_options.sandbox)
|
129
214
|
|
215
|
+
# Sanitization parameters.
|
216
|
+
sanitized = False
|
217
|
+
if is_executable_sanitized(executable):
|
218
|
+
# Remove any memory constraints for a sanitized executable.
|
219
|
+
# Sanitizers are known to be memory-hungry.
|
220
|
+
sandbox_params.address_space = None
|
221
|
+
|
222
|
+
# Reset timeout configs since sanitizers are known to be time-hungry.
|
223
|
+
sandbox_params.timeout = None
|
224
|
+
sandbox_params.wallclock_timeout = None
|
225
|
+
sanitized = True
|
226
|
+
|
130
227
|
sandbox_params.set_stdall(
|
131
228
|
stdin=PosixPath(file_mapping.input) if stdin is not None else None,
|
132
229
|
stdout=PosixPath(file_mapping.output) if stdout is not None else None,
|
133
|
-
stderr=PosixPath(file_mapping.error)
|
230
|
+
stderr=PosixPath(file_mapping.error)
|
231
|
+
if stderr is not None or sanitized
|
232
|
+
else None,
|
134
233
|
)
|
135
234
|
|
136
235
|
assert execution_options.command
|
137
236
|
command = get_mapped_command(execution_options.command, file_mapping)
|
237
|
+
command = substitute_commands([command], sanitized=sanitized)[0]
|
138
238
|
|
139
239
|
if extra_args is not None:
|
140
240
|
splitted_command = shlex.split(command)
|
@@ -175,11 +275,21 @@ def run_item(
|
|
175
275
|
if outputs:
|
176
276
|
artifacts.outputs.extend(outputs)
|
177
277
|
|
178
|
-
|
278
|
+
run_log = steps_with_caching.run(
|
179
279
|
command,
|
180
280
|
params=sandbox_params,
|
181
281
|
sandbox=sandbox,
|
182
282
|
artifacts=artifacts,
|
183
283
|
dependency_cache=dependency_cache,
|
184
|
-
metadata=RunLogMetadata(language=code.language),
|
284
|
+
metadata=RunLogMetadata(language=code.language, is_sanitized=sanitized),
|
185
285
|
)
|
286
|
+
|
287
|
+
# Find sanitizer logs.
|
288
|
+
if run_log is not None and run_log.warnings:
|
289
|
+
assert sandbox_params.stderr_file is not None
|
290
|
+
stderr_output = artifacts.get_output_file_for_src(sandbox_params.stderr_file)
|
291
|
+
if stderr_output is not None:
|
292
|
+
warning_stack.get_warning_stack().add_sanitizer_warning(
|
293
|
+
package.get_cache_storage(), code, stderr_output
|
294
|
+
)
|
295
|
+
return run_log
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import pathlib
|
2
|
+
|
3
|
+
import typer
|
4
|
+
|
5
|
+
from rbx import annotations, console
|
6
|
+
from rbx.box import code, package
|
7
|
+
from rbx.box.code import SanitizationLevel
|
8
|
+
from rbx.box.sanitizers import warning_stack
|
9
|
+
from rbx.box.schema import CodeItem
|
10
|
+
|
11
|
+
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
12
|
+
|
13
|
+
|
14
|
+
def _compile_out():
|
15
|
+
return package.get_build_path() / 'exe'
|
16
|
+
|
17
|
+
|
18
|
+
def _compile(item: CodeItem, sanitized: SanitizationLevel, warnings: bool):
|
19
|
+
console.console.print(f'Compiling [item]{item.path}[/item]...')
|
20
|
+
digest = code.compile_item(item, sanitized, force_warnings=warnings, verbose=True)
|
21
|
+
cacher = package.get_file_cacher()
|
22
|
+
out_path = _compile_out()
|
23
|
+
cacher.get_file_to_path(digest, out_path)
|
24
|
+
out_path.chmod(0o755)
|
25
|
+
|
26
|
+
# Clear the warning stack as we don't really worry about it when running
|
27
|
+
# `rbx compile`.
|
28
|
+
warning_stack.get_warning_stack().clear()
|
29
|
+
|
30
|
+
console.console.print(
|
31
|
+
f'[success]Compiled file written at [item]{out_path}[/item][/success]'
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
def any(path: str, sanitized: bool = False, warnings: bool = False):
|
36
|
+
pkg = package.find_problem_package_or_die()
|
37
|
+
|
38
|
+
solution = package.get_solution_or_nil(path)
|
39
|
+
if solution is not None:
|
40
|
+
_compile(
|
41
|
+
solution,
|
42
|
+
sanitized=SanitizationLevel.FORCE if sanitized else SanitizationLevel.NONE,
|
43
|
+
warnings=warnings,
|
44
|
+
)
|
45
|
+
return
|
46
|
+
|
47
|
+
for generator in pkg.generators:
|
48
|
+
if generator.path == pathlib.Path(path) or generator.name == path:
|
49
|
+
_compile(
|
50
|
+
generator,
|
51
|
+
sanitized=SanitizationLevel.FORCE
|
52
|
+
if sanitized
|
53
|
+
else SanitizationLevel.PREFER,
|
54
|
+
warnings=warnings,
|
55
|
+
)
|
56
|
+
return
|
57
|
+
|
58
|
+
if pkg.checker is not None and pkg.checker.path == pathlib.Path(path):
|
59
|
+
_compile(
|
60
|
+
pkg.checker,
|
61
|
+
sanitized=SanitizationLevel.FORCE
|
62
|
+
if sanitized
|
63
|
+
else SanitizationLevel.PREFER,
|
64
|
+
warnings=warnings,
|
65
|
+
)
|
66
|
+
return
|
67
|
+
|
68
|
+
if pkg.validator is not None and pkg.validator.path == pathlib.Path(path):
|
69
|
+
_compile(
|
70
|
+
pkg.validator,
|
71
|
+
sanitized=SanitizationLevel.FORCE
|
72
|
+
if sanitized
|
73
|
+
else SanitizationLevel.PREFER,
|
74
|
+
warnings=warnings,
|
75
|
+
)
|
76
|
+
return
|
77
|
+
|
78
|
+
_compile(
|
79
|
+
CodeItem(path=pathlib.Path(path)),
|
80
|
+
sanitized=SanitizationLevel.FORCE if sanitized else SanitizationLevel.NONE,
|
81
|
+
warnings=warnings,
|
82
|
+
)
|
@@ -6,8 +6,8 @@ from typing import List, Optional, Tuple
|
|
6
6
|
|
7
7
|
import typer
|
8
8
|
|
9
|
-
from rbx import console, testing_utils
|
10
|
-
from rbx.box
|
9
|
+
from rbx import console, testing_utils
|
10
|
+
from rbx.box import cd, package
|
11
11
|
from rbx.box.contest.contest_package import get_problems
|
12
12
|
from rbx.box.contest.schema import Contest, ContestProblem, ContestStatement
|
13
13
|
from rbx.box.schema import Package, Testcase
|
@@ -58,8 +58,8 @@ class ExtractedProblem:
|
|
58
58
|
|
59
59
|
|
60
60
|
def _get_samples(problem: ContestProblem) -> List[Testcase]:
|
61
|
-
with
|
62
|
-
|
61
|
+
with cd.new_package_cd(problem.get_path()):
|
62
|
+
package.clear_package_cache()
|
63
63
|
return get_samples()
|
64
64
|
|
65
65
|
|
@@ -156,8 +156,8 @@ def _build_problem_statements(
|
|
156
156
|
console.console.print(
|
157
157
|
f'Building statement for problem {extracted_problem.problem.short_name}...'
|
158
158
|
)
|
159
|
-
with
|
160
|
-
|
159
|
+
with cd.new_package_cd(extracted_problem.problem.get_path()):
|
160
|
+
package.clear_package_cache()
|
161
161
|
# TODO: respect steps override
|
162
162
|
content, _ = build_statements.build_statement_bytes(
|
163
163
|
extracted_problem.statement,
|
@@ -282,6 +282,7 @@ def build_statement_rooted(
|
|
282
282
|
|
283
283
|
if joiner is None:
|
284
284
|
return last_content, last_output
|
285
|
+
assert statement.joiner is not None
|
285
286
|
|
286
287
|
# Join statements.
|
287
288
|
console.console.print('Joining statements...')
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import functools
|
2
2
|
import pathlib
|
3
|
-
from typing import List, Optional
|
3
|
+
from typing import List, Optional, Tuple
|
4
4
|
|
5
|
+
import ruyaml
|
5
6
|
import typer
|
6
7
|
from pydantic import ValidationError
|
7
8
|
|
8
9
|
from rbx import console, utils
|
10
|
+
from rbx.box import cd
|
9
11
|
from rbx.box.contest.schema import Contest
|
10
12
|
from rbx.box.package import find_problem_package_or_die, warn_preset_deactivated
|
11
13
|
from rbx.box.schema import Package
|
@@ -58,7 +60,7 @@ def find_contest(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
|
58
60
|
def within_contest(func):
|
59
61
|
@functools.wraps(func)
|
60
62
|
def wrapper(*args, **kwargs):
|
61
|
-
with
|
63
|
+
with cd.new_package_cd(find_contest()):
|
62
64
|
return func(*args, **kwargs)
|
63
65
|
|
64
66
|
return wrapper
|
@@ -80,3 +82,14 @@ def get_problems(contest: Contest) -> List[Package]:
|
|
80
82
|
for problem in contest.problems:
|
81
83
|
problems.append(find_problem_package_or_die(problem.get_path()))
|
82
84
|
return problems
|
85
|
+
|
86
|
+
|
87
|
+
def get_ruyaml() -> Tuple[ruyaml.YAML, ruyaml.Any]:
|
88
|
+
contest_yaml_path = find_contest_yaml()
|
89
|
+
if contest_yaml_path is None:
|
90
|
+
console.console.print(
|
91
|
+
f'Contest not found in {pathlib.Path().absolute()}', style='error'
|
92
|
+
)
|
93
|
+
raise typer.Exit(1)
|
94
|
+
res = ruyaml.YAML()
|
95
|
+
return res, res.load(contest_yaml_path.read_text())
|
@@ -2,15 +2,6 @@ from rbx.box import environment, package
|
|
2
2
|
from rbx.box.contest import contest_package
|
3
3
|
|
4
4
|
|
5
|
-
def clear_package_cache():
|
6
|
-
pkgs = [package]
|
7
|
-
|
8
|
-
for pkg in pkgs:
|
9
|
-
for fn in pkg.__dict__.values():
|
10
|
-
if hasattr(fn, 'cache_clear'):
|
11
|
-
fn.cache_clear()
|
12
|
-
|
13
|
-
|
14
5
|
def clear_all_caches():
|
15
6
|
pkgs = [package, environment, contest_package]
|
16
7
|
|
@@ -7,13 +7,12 @@ import rich.prompt
|
|
7
7
|
import typer
|
8
8
|
|
9
9
|
from rbx import annotations, console, utils
|
10
|
-
from rbx.box import creation, presets
|
11
|
-
from rbx.box.contest import contest_utils, statements
|
10
|
+
from rbx.box import cd, creation, presets
|
11
|
+
from rbx.box.contest import contest_package, contest_utils, statements
|
12
12
|
from rbx.box.contest.contest_package import (
|
13
13
|
find_contest,
|
14
14
|
find_contest_package_or_die,
|
15
15
|
find_contest_yaml,
|
16
|
-
save_contest,
|
17
16
|
within_contest,
|
18
17
|
)
|
19
18
|
from rbx.box.contest.schema import ContestProblem
|
@@ -111,7 +110,7 @@ def create(
|
|
111
110
|
if local:
|
112
111
|
shutil.copytree(str(preset_path), str(dest_path / '.local.rbx'))
|
113
112
|
|
114
|
-
with
|
113
|
+
with cd.new_package_cd(dest_path):
|
115
114
|
contest_utils.clear_all_caches()
|
116
115
|
presets.generate_lock(preset if not local else presets.LOCAL)
|
117
116
|
|
@@ -145,16 +144,19 @@ def add(path: str, short_name: str, preset: Optional[str] = None):
|
|
145
144
|
creation.create(name, preset=preset, path=pathlib.Path(path))
|
146
145
|
|
147
146
|
contest = find_contest_package_or_die()
|
148
|
-
|
149
|
-
contest
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
147
|
+
|
148
|
+
ru, contest = contest_package.get_ruyaml()
|
149
|
+
|
150
|
+
contest['problems'].append(
|
151
|
+
{
|
152
|
+
'short_name': short_name,
|
153
|
+
'path': path,
|
154
|
+
}
|
155
155
|
)
|
156
|
+
dest = find_contest_yaml()
|
157
|
+
assert dest is not None
|
158
|
+
utils.save_ruyaml(dest, ru, contest)
|
156
159
|
|
157
|
-
save_contest(contest)
|
158
160
|
console.console.print(
|
159
161
|
f'Problem [item]{name} ({short_name})[/item] added to contest at [item]{path}[/item].'
|
160
162
|
)
|
@@ -165,25 +167,34 @@ def add(path: str, short_name: str, preset: Optional[str] = None):
|
|
165
167
|
def remove(path_or_short_name: str):
|
166
168
|
contest = find_contest_package_or_die()
|
167
169
|
|
168
|
-
|
169
|
-
|
170
|
-
for problem in contest.problems:
|
170
|
+
removed_problem_idx = None
|
171
|
+
removed_problem = None
|
172
|
+
for i, problem in enumerate(contest.problems):
|
171
173
|
if (
|
172
174
|
problem.path == pathlib.Path(path_or_short_name)
|
173
175
|
or problem.short_name == path_or_short_name
|
174
176
|
):
|
175
|
-
|
176
|
-
|
177
|
-
|
177
|
+
removed_problem_idx = i
|
178
|
+
removed_problem = problem
|
179
|
+
break
|
178
180
|
|
179
|
-
|
180
|
-
save_contest(contest)
|
181
|
-
|
182
|
-
for problem in removed_problems:
|
183
|
-
shutil.rmtree(str(problem.path), ignore_errors=True)
|
181
|
+
if removed_problem_idx is None or removed_problem is None:
|
184
182
|
console.console.print(
|
185
|
-
f'Problem [item]{
|
183
|
+
f'[error]Problem [item]{path_or_short_name}[/item] not found in contest.[/error]'
|
186
184
|
)
|
185
|
+
raise typer.Exit(1)
|
186
|
+
|
187
|
+
ru, contest = contest_package.get_ruyaml()
|
188
|
+
|
189
|
+
del contest['problems'][removed_problem_idx]
|
190
|
+
dest = find_contest_yaml()
|
191
|
+
assert dest is not None
|
192
|
+
utils.save_ruyaml(dest, ru, contest)
|
193
|
+
|
194
|
+
shutil.rmtree(str(removed_problem.path), ignore_errors=True)
|
195
|
+
console.console.print(
|
196
|
+
f'Problem [item]{removed_problem.short_name}[/item] removed from contest at [item]{removed_problem.path}[/item].'
|
197
|
+
)
|
187
198
|
|
188
199
|
|
189
200
|
@app.command(
|