rbx.cp 0.13.3__py3-none-any.whl → 0.13.4__py3-none-any.whl
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/annotations.py +5 -5
- rbx/box/checkers.py +24 -13
- rbx/box/cli.py +1 -4
- rbx/box/contest/build_contest_statements.py +16 -3
- rbx/box/contest/schema.py +1 -2
- rbx/box/fields.py +25 -1
- rbx/box/generators.py +5 -2
- rbx/box/global_package.py +5 -1
- rbx/box/header.py +19 -11
- rbx/box/package.py +3 -1
- rbx/box/presets/__init__.py +2 -2
- rbx/box/schema.py +4 -25
- rbx/box/statements/build_statements.py +5 -1
- rbx/box/statements/builders.py +7 -7
- rbx/box/statements/schema.py +11 -2
- rbx/box/testcase_utils.py +2 -0
- rbx/box/testing/__init__.py +0 -0
- rbx/box/testing/testing_package.py +241 -0
- rbx/box/testing/testing_preset.py +36 -0
- rbx/box/testing/testing_shared.py +81 -0
- rbx/box/validators.py +2 -1
- rbx/grading/caching.py +3 -2
- rbx/resources/presets/default/shared/contest_template.rbx.tex +1 -1
- rbx/resources/presets/default/shared/problem_template.rbx.tex +5 -1
- rbx/testing_utils.py +1 -1
- rbx/utils.py +8 -0
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.4.dist-info}/METADATA +2 -1
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.4.dist-info}/RECORD +31 -49
- rbx/box/conftest.py +0 -42
- rbx/box/generators_test.py +0 -67
- rbx/box/lazy_importing_test.py +0 -25
- rbx/box/solutions_test.py +0 -47
- rbx/box/validators_test.py +0 -15
- rbx/checker.py +0 -128
- rbx/clone.py +0 -197
- rbx/conftest.py +0 -38
- rbx/create.py +0 -37
- rbx/edit.py +0 -24
- rbx/grading/conftest.py +0 -33
- rbx/grading/steps_with_caching_run_test.py +0 -707
- rbx/grading_utils.py +0 -148
- rbx/hydration.py +0 -101
- rbx/main.py +0 -118
- rbx/metadata.py +0 -105
- rbx/run.py +0 -45
- rbx/schema.py +0 -64
- rbx/submit.py +0 -61
- rbx/test.py +0 -349
- rbx/testcase.py +0 -70
- rbx/testcase_rendering.py +0 -79
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.4.dist-info}/LICENSE +0 -0
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.4.dist-info}/WHEEL +0 -0
- {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.4.dist-info}/entry_points.txt +0 -0
rbx/grading_utils.py
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
import pathlib
|
2
|
-
from pathlib import PosixPath
|
3
|
-
from typing import List
|
4
|
-
|
5
|
-
from rbx import config
|
6
|
-
from rbx.config import Artifact, Language, format_vars
|
7
|
-
from rbx.grading import steps
|
8
|
-
from rbx.grading.judge.sandbox import MERGE_STDERR, SandboxParams
|
9
|
-
from rbx.grading.steps import (
|
10
|
-
GradingArtifacts,
|
11
|
-
GradingFileInput,
|
12
|
-
GradingFileOutput,
|
13
|
-
TestcaseIO,
|
14
|
-
)
|
15
|
-
from rbx.schema import DumpedProblem, Problem
|
16
|
-
|
17
|
-
|
18
|
-
def build_formatted_command(
|
19
|
-
command: str, problem: DumpedProblem, lang: Language
|
20
|
-
) -> str:
|
21
|
-
return format_vars(
|
22
|
-
command,
|
23
|
-
**problem.get_vars(),
|
24
|
-
file=lang.get_file(problem.code),
|
25
|
-
submit_file=lang.get_submit_file(problem.code),
|
26
|
-
)
|
27
|
-
|
28
|
-
|
29
|
-
def build_preprocess_commands(problem: DumpedProblem, lang: Language) -> List[str]:
|
30
|
-
return [
|
31
|
-
build_formatted_command(cmd, problem, lang) for cmd in (lang.preprocess or [])
|
32
|
-
]
|
33
|
-
|
34
|
-
|
35
|
-
def build_preprocess_sandbox_params() -> SandboxParams:
|
36
|
-
params = SandboxParams(
|
37
|
-
max_processes=None,
|
38
|
-
preserve_env=True,
|
39
|
-
)
|
40
|
-
params.add_mapped_directory(pathlib.Path('/usr'))
|
41
|
-
params.add_mapped_directory(pathlib.Path('/etc'))
|
42
|
-
return params
|
43
|
-
|
44
|
-
|
45
|
-
def build_compile_grading_artifacts(
|
46
|
-
problem: DumpedProblem, lang: Language
|
47
|
-
) -> GradingArtifacts:
|
48
|
-
res = GradingArtifacts(root=PosixPath('.'))
|
49
|
-
file = lang.get_file(problem.code)
|
50
|
-
submit_file = lang.get_submit_file(problem.code)
|
51
|
-
# Copy input file.
|
52
|
-
res.inputs.append(GradingFileInput(src=PosixPath(file), dest=PosixPath(file)))
|
53
|
-
# Copy output file.
|
54
|
-
if lang.has_submit_file():
|
55
|
-
res.outputs.append(
|
56
|
-
GradingFileOutput(
|
57
|
-
src=PosixPath(submit_file),
|
58
|
-
dest=PosixPath(submit_file),
|
59
|
-
)
|
60
|
-
)
|
61
|
-
# Copy other artifacts.
|
62
|
-
for artifact_name, artifact_cfg in lang.artifacts.items():
|
63
|
-
artifact_cfg = artifact_cfg or Artifact()
|
64
|
-
artifact_path = format_vars(
|
65
|
-
artifact_name, **problem.get_vars(), file=file, submit_file=submit_file
|
66
|
-
)
|
67
|
-
res.outputs.append(
|
68
|
-
GradingFileOutput(
|
69
|
-
src=PosixPath(artifact_path),
|
70
|
-
dest=PosixPath(artifact_cfg.filename or artifact_path),
|
71
|
-
optional=artifact_cfg.optional,
|
72
|
-
executable=artifact_cfg.executable,
|
73
|
-
)
|
74
|
-
)
|
75
|
-
|
76
|
-
return res
|
77
|
-
|
78
|
-
|
79
|
-
def build_run_sandbox_params(problem: Problem, has_input: bool) -> SandboxParams:
|
80
|
-
params = SandboxParams()
|
81
|
-
params.timeout = problem.timeLimit * 2
|
82
|
-
params.wallclock_timeout = problem.timeLimit * 5
|
83
|
-
params.address_space = problem.memoryLimit or 1024 # 1 GB
|
84
|
-
params.set_stdall(
|
85
|
-
stdin=PosixPath('stdin.txt') if has_input else None,
|
86
|
-
stdout=PosixPath('stdout.txt'),
|
87
|
-
stderr=MERGE_STDERR,
|
88
|
-
)
|
89
|
-
return params
|
90
|
-
|
91
|
-
|
92
|
-
def build_run_grading_artifacts(
|
93
|
-
testcase: TestcaseIO, persist_root: pathlib.Path
|
94
|
-
) -> GradingArtifacts:
|
95
|
-
res = GradingArtifacts(root=PosixPath('.'))
|
96
|
-
res.inputs.append(
|
97
|
-
GradingFileInput(
|
98
|
-
src=testcase.input,
|
99
|
-
dest=PosixPath('stdin.txt'),
|
100
|
-
)
|
101
|
-
)
|
102
|
-
res.outputs.append(
|
103
|
-
GradingFileOutput(
|
104
|
-
src=PosixPath('stdout.txt'),
|
105
|
-
dest=persist_root / f'stdout-{testcase.index}.txt',
|
106
|
-
maxlen=steps.MAX_STDOUT_LEN,
|
107
|
-
)
|
108
|
-
)
|
109
|
-
return res
|
110
|
-
|
111
|
-
|
112
|
-
def build_checker_compile_grading_artifacts(
|
113
|
-
problem: DumpedProblem, persist_root: pathlib.Path
|
114
|
-
) -> GradingArtifacts:
|
115
|
-
res = GradingArtifacts(root=PosixPath('.'))
|
116
|
-
if not problem.checker:
|
117
|
-
return res
|
118
|
-
|
119
|
-
checker_path = PosixPath(problem.checker)
|
120
|
-
if not checker_path.is_file():
|
121
|
-
checker_path = config.get_builtin_checker(problem.checker)
|
122
|
-
if not checker_path:
|
123
|
-
return res
|
124
|
-
|
125
|
-
res.inputs.append(GradingFileInput(src=checker_path, dest=PosixPath('checker.cpp')))
|
126
|
-
testlib = config.get_testlib()
|
127
|
-
if testlib.is_file():
|
128
|
-
res.inputs.append(GradingFileInput(src=testlib, dest=PosixPath('testlib.h')))
|
129
|
-
res.outputs.append(
|
130
|
-
GradingFileOutput(
|
131
|
-
src=PosixPath('checker'), dest=persist_root / 'checker', executable=True
|
132
|
-
)
|
133
|
-
)
|
134
|
-
return res
|
135
|
-
|
136
|
-
|
137
|
-
def build_checker_run_grading_artifacts(
|
138
|
-
problem: DumpedProblem, persist_root: pathlib.Path
|
139
|
-
) -> GradingArtifacts:
|
140
|
-
res = GradingArtifacts(root=PosixPath('.'))
|
141
|
-
if not problem.checker:
|
142
|
-
return res
|
143
|
-
res.inputs.append(
|
144
|
-
GradingFileInput(
|
145
|
-
src=persist_root / 'checker', dest=PosixPath('checker'), executable=True
|
146
|
-
)
|
147
|
-
)
|
148
|
-
return res
|
rbx/hydration.py
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
import pathlib
|
2
|
-
from typing import List, Optional, Tuple
|
3
|
-
|
4
|
-
from rbx import config, hydration, metadata
|
5
|
-
from rbx.console import console
|
6
|
-
from rbx.schema import DumpedProblem, Testcase
|
7
|
-
from rbx.test import get_testcases_io
|
8
|
-
|
9
|
-
|
10
|
-
def get_testcase_paths(
|
11
|
-
root: pathlib.Path, problem: DumpedProblem, i: int
|
12
|
-
) -> Tuple[pathlib.Path, pathlib.Path]:
|
13
|
-
return (root / f'{problem.code}.{i}.in', root / f'{problem.code}.{i}.out')
|
14
|
-
|
15
|
-
|
16
|
-
def hydrate_problem(root: pathlib.Path, problem: DumpedProblem):
|
17
|
-
for i, testcase in enumerate(problem.tests or []):
|
18
|
-
in_path, out_path = get_testcase_paths(root, problem, i)
|
19
|
-
in_path.write_text(testcase.input)
|
20
|
-
out_path.write_text(testcase.output)
|
21
|
-
|
22
|
-
|
23
|
-
def add_testcase(root: pathlib.Path, problem: DumpedProblem, testcase: Testcase):
|
24
|
-
problem_path = metadata.find_problem_path_by_code(problem.code, root)
|
25
|
-
if not problem_path or not problem_path.is_file():
|
26
|
-
console.print(
|
27
|
-
f'[error]Problem [item]{problem.pretty_name()}[/item] not found.[/error]'
|
28
|
-
)
|
29
|
-
return
|
30
|
-
|
31
|
-
# Pick next number.
|
32
|
-
i = max([tc.index for tc in get_testcases_io(problem, root)] + [-1]) + 1
|
33
|
-
in_path, out_path = get_testcase_paths(root, problem, i)
|
34
|
-
in_path.write_text(testcase.input)
|
35
|
-
out_path.write_text(testcase.output)
|
36
|
-
|
37
|
-
console.print(
|
38
|
-
f'Added testcase [item]{i}[/item] to problem [item]{problem.pretty_name()}[/item].'
|
39
|
-
)
|
40
|
-
|
41
|
-
|
42
|
-
def remove_testcase(root: pathlib.Path, problem: DumpedProblem, i: int):
|
43
|
-
problem_path = metadata.find_problem_path_by_code(problem.code, root)
|
44
|
-
if not problem_path or not problem_path.is_file():
|
45
|
-
console.print(
|
46
|
-
f'[error]Problem [item]{problem.pretty_name()}[/item] not found.[/error]'
|
47
|
-
)
|
48
|
-
return
|
49
|
-
|
50
|
-
testcases = get_testcases_io(problem, root)
|
51
|
-
testcases = [testcase for testcase in testcases if testcase.index == i]
|
52
|
-
if not testcases:
|
53
|
-
console.print(
|
54
|
-
f'[error]Testcase [item]{i}[/item] not found in problem [item]{problem.pretty_name()}[/item].[/error]'
|
55
|
-
)
|
56
|
-
return
|
57
|
-
if testcases[0].input:
|
58
|
-
testcases[0].input.unlink(missing_ok=True)
|
59
|
-
if testcases[0].output:
|
60
|
-
testcases[0].output.unlink(missing_ok=True)
|
61
|
-
|
62
|
-
console.print(
|
63
|
-
f'Removed testcase [item]{i}[/item] from problem [item]{problem.pretty_name()}[/item].'
|
64
|
-
)
|
65
|
-
|
66
|
-
|
67
|
-
def edit_testcase(root: pathlib.Path, problem: DumpedProblem, i: int):
|
68
|
-
problem_path = metadata.find_problem_path_by_code(problem.code, root)
|
69
|
-
if not problem_path or not problem_path.is_file():
|
70
|
-
console.print(
|
71
|
-
f'[error]Problem [item]{problem.pretty_name()}[/item] not found.[/error]'
|
72
|
-
)
|
73
|
-
return
|
74
|
-
|
75
|
-
testcases = get_testcases_io(problem, root)
|
76
|
-
testcases = [testcase for testcase in testcases if testcase.index == i]
|
77
|
-
if not testcases:
|
78
|
-
console.print(
|
79
|
-
f'[error]Testcase [item]{i}[/item] not found in problem [item]{problem.pretty_name()}[/item].[/error]'
|
80
|
-
)
|
81
|
-
return
|
82
|
-
|
83
|
-
paths: List[Optional[pathlib.Path]] = [testcases[0].input, testcases[0].output]
|
84
|
-
config.open_editor(*[path for path in paths if path is not None and path.is_file()])
|
85
|
-
|
86
|
-
|
87
|
-
def main(problem: Optional[str] = None):
|
88
|
-
problems_to_hydrate = []
|
89
|
-
if not problem:
|
90
|
-
problems_to_hydrate = metadata.find_problems()
|
91
|
-
else:
|
92
|
-
dumped_problem = metadata.find_problem_by_anything(problem)
|
93
|
-
problems_to_hydrate.append(dumped_problem)
|
94
|
-
|
95
|
-
root = pathlib.Path()
|
96
|
-
|
97
|
-
for dumped_problem in problems_to_hydrate:
|
98
|
-
console.print(
|
99
|
-
f'Hydrating problem [item]{dumped_problem.pretty_name()}[/item]...'
|
100
|
-
)
|
101
|
-
hydration.hydrate_problem(root, dumped_problem)
|
rbx/main.py
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
# flake8: noqa
|
2
|
-
import typer
|
3
|
-
from typing_extensions import Annotated
|
4
|
-
|
5
|
-
from rbx import annotations, checker, config, testcase
|
6
|
-
from rbx import clone as clone_pkg
|
7
|
-
from rbx import create as create_pkg
|
8
|
-
from rbx import edit as edit_pkg
|
9
|
-
from rbx import run as run_pkg
|
10
|
-
from rbx import submit as submit_pkg
|
11
|
-
from rbx import test as test_pkg
|
12
|
-
from rbx.box import main
|
13
|
-
|
14
|
-
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
15
|
-
app.add_typer(main.app, name='box', cls=annotations.AliasGroup)
|
16
|
-
app.add_typer(
|
17
|
-
config.app,
|
18
|
-
name='config, cfg',
|
19
|
-
cls=annotations.AliasGroup,
|
20
|
-
help='Manage the configuration of the tool.',
|
21
|
-
)
|
22
|
-
app.add_typer(
|
23
|
-
testcase.app,
|
24
|
-
name='testcase, tc',
|
25
|
-
cls=annotations.AliasGroup,
|
26
|
-
help='Commands to manage the testcases of a problem.',
|
27
|
-
)
|
28
|
-
app.add_typer(
|
29
|
-
checker.app,
|
30
|
-
name='checker, check',
|
31
|
-
cls=annotations.AliasGroup,
|
32
|
-
help='Commands to manage the checker of a problem.',
|
33
|
-
)
|
34
|
-
|
35
|
-
|
36
|
-
@app.command('clone, c')
|
37
|
-
def clone(lang: annotations.Language):
|
38
|
-
"""
|
39
|
-
Clones by waiting for a set of problems to be sent through Competitive Companion.
|
40
|
-
"""
|
41
|
-
clone_pkg.main(lang=lang)
|
42
|
-
|
43
|
-
|
44
|
-
@app.command('new, n')
|
45
|
-
def new(
|
46
|
-
name: str,
|
47
|
-
language: annotations.Language,
|
48
|
-
timelimit: annotations.Timelimit = 1000,
|
49
|
-
memorylimit: annotations.Memorylimit = 256,
|
50
|
-
multitest: annotations.Multitest = False,
|
51
|
-
):
|
52
|
-
"""
|
53
|
-
Create a new problem from scratch.
|
54
|
-
"""
|
55
|
-
create_pkg.main(name, language, timelimit, memorylimit, multitest)
|
56
|
-
|
57
|
-
|
58
|
-
@app.command('edit, e')
|
59
|
-
def edit(
|
60
|
-
problem: annotations.Problem, language: annotations.LanguageWithDefault = None
|
61
|
-
):
|
62
|
-
"""
|
63
|
-
Edit the code of a problem using the provided language.
|
64
|
-
"""
|
65
|
-
edit_pkg.main(problem, language)
|
66
|
-
|
67
|
-
|
68
|
-
@app.command('test, t')
|
69
|
-
def test(
|
70
|
-
problem: annotations.Problem,
|
71
|
-
language: annotations.LanguageWithDefault = None,
|
72
|
-
keep_sandbox: bool = False,
|
73
|
-
index: annotations.TestcaseIndex = None,
|
74
|
-
interactive: Annotated[bool, typer.Option('--interactive', '--int')] = False,
|
75
|
-
):
|
76
|
-
"""
|
77
|
-
Test a problem using the provided language.
|
78
|
-
"""
|
79
|
-
test_pkg.main(
|
80
|
-
problem,
|
81
|
-
language,
|
82
|
-
keep_sandbox=keep_sandbox,
|
83
|
-
index=index,
|
84
|
-
interactive=interactive,
|
85
|
-
)
|
86
|
-
|
87
|
-
|
88
|
-
@app.command('run, r')
|
89
|
-
def run(
|
90
|
-
problem: annotations.Problem,
|
91
|
-
language: annotations.LanguageWithDefault = None,
|
92
|
-
keep_sandbox: bool = False,
|
93
|
-
):
|
94
|
-
"""
|
95
|
-
Run a problem using the provided language.
|
96
|
-
"""
|
97
|
-
run_pkg.main(
|
98
|
-
problem,
|
99
|
-
language,
|
100
|
-
keep_sandbox=keep_sandbox,
|
101
|
-
)
|
102
|
-
|
103
|
-
|
104
|
-
@app.command('submit, s')
|
105
|
-
def submit(
|
106
|
-
problem: annotations.Problem,
|
107
|
-
language: annotations.LanguageWithDefault = None,
|
108
|
-
keep_sandbox: bool = False,
|
109
|
-
):
|
110
|
-
"""
|
111
|
-
Submit a problem using the provided language.
|
112
|
-
"""
|
113
|
-
submit_pkg.main(problem, language, keep_sandbox=keep_sandbox)
|
114
|
-
|
115
|
-
|
116
|
-
@app.callback()
|
117
|
-
def callback():
|
118
|
-
pass
|
rbx/metadata.py
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
import pathlib
|
2
|
-
from typing import List, Optional, Tuple
|
3
|
-
|
4
|
-
from rbx.schema import DumpedProblem
|
5
|
-
|
6
|
-
|
7
|
-
def _normalize_alias(alias: str) -> str:
|
8
|
-
return alias.lower()
|
9
|
-
|
10
|
-
|
11
|
-
def _find_alias(alias: str, haystack: List[str]) -> Optional[int]:
|
12
|
-
normalized_alias = _normalize_alias(alias)
|
13
|
-
for i, candidate in enumerate(haystack):
|
14
|
-
if _normalize_alias(candidate) == normalized_alias:
|
15
|
-
return i
|
16
|
-
return None
|
17
|
-
|
18
|
-
|
19
|
-
def _get_best_alias_from_candidates(
|
20
|
-
alias: str, candidates: List[Tuple[pathlib.Path, DumpedProblem]]
|
21
|
-
) -> Optional[Tuple[pathlib.Path, DumpedProblem]]:
|
22
|
-
best_priority = 1e9
|
23
|
-
best_candidates = []
|
24
|
-
for path, problem in candidates:
|
25
|
-
index = _find_alias(alias, problem.aliases)
|
26
|
-
if index is None:
|
27
|
-
continue
|
28
|
-
if index < best_priority:
|
29
|
-
best_priority = index
|
30
|
-
best_candidates = [(path, problem)]
|
31
|
-
elif index == best_priority:
|
32
|
-
best_candidates.append((path, problem))
|
33
|
-
|
34
|
-
if len(best_candidates) != 1:
|
35
|
-
# TODO
|
36
|
-
return None
|
37
|
-
|
38
|
-
return best_candidates[0]
|
39
|
-
|
40
|
-
|
41
|
-
def find_problem_path_by_code(
|
42
|
-
code: str, root: Optional[pathlib.Path] = None
|
43
|
-
) -> Optional[pathlib.Path]:
|
44
|
-
if not root:
|
45
|
-
root = pathlib.Path()
|
46
|
-
|
47
|
-
metadata_path = root / f'{code}.rbx.json'
|
48
|
-
if not metadata_path.is_file():
|
49
|
-
return None
|
50
|
-
return metadata_path
|
51
|
-
|
52
|
-
|
53
|
-
def find_problem_path_by_alias(
|
54
|
-
alias: str, root: Optional[pathlib.Path] = None
|
55
|
-
) -> Optional[pathlib.Path]:
|
56
|
-
if not root:
|
57
|
-
root = pathlib.Path()
|
58
|
-
|
59
|
-
candidates: List[Tuple[pathlib.Path, DumpedProblem]] = []
|
60
|
-
for metadata_path in root.glob('*.rbx.json'):
|
61
|
-
problem = DumpedProblem.model_validate_json(metadata_path.read_text())
|
62
|
-
if _find_alias(alias, problem.aliases) is not None:
|
63
|
-
candidates.append((metadata_path, problem))
|
64
|
-
|
65
|
-
picked_candidate = _get_best_alias_from_candidates(alias, candidates)
|
66
|
-
if not picked_candidate:
|
67
|
-
return None
|
68
|
-
return picked_candidate[0]
|
69
|
-
|
70
|
-
|
71
|
-
def find_problem_by_alias(
|
72
|
-
alias: str, root: Optional[pathlib.Path] = None
|
73
|
-
) -> Optional[DumpedProblem]:
|
74
|
-
metadata_path = find_problem_path_by_alias(alias, root)
|
75
|
-
if not metadata_path:
|
76
|
-
return None
|
77
|
-
return DumpedProblem.model_validate_json(metadata_path.read_text())
|
78
|
-
|
79
|
-
|
80
|
-
def find_problem_by_code(
|
81
|
-
code: str, root: Optional[pathlib.Path] = None
|
82
|
-
) -> Optional[DumpedProblem]:
|
83
|
-
metadata_path = find_problem_path_by_code(code, root)
|
84
|
-
if not metadata_path:
|
85
|
-
return None
|
86
|
-
return DumpedProblem.model_validate_json(metadata_path.read_text())
|
87
|
-
|
88
|
-
|
89
|
-
def find_problem_by_anything(
|
90
|
-
anything: str, root: Optional[pathlib.Path] = None
|
91
|
-
) -> Optional[DumpedProblem]:
|
92
|
-
problem = find_problem_by_code(anything, root)
|
93
|
-
if problem:
|
94
|
-
return problem
|
95
|
-
return find_problem_by_alias(anything, root)
|
96
|
-
|
97
|
-
|
98
|
-
def find_problems(root: Optional[pathlib.Path] = None) -> List[DumpedProblem]:
|
99
|
-
if not root:
|
100
|
-
root = pathlib.Path()
|
101
|
-
|
102
|
-
problems = []
|
103
|
-
for metadata_path in root.glob('*.rbx.json'):
|
104
|
-
problems.append(DumpedProblem.model_validate_json(metadata_path.read_text()))
|
105
|
-
return problems
|
rbx/run.py
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
import atexit
|
2
|
-
import os
|
3
|
-
import shlex
|
4
|
-
|
5
|
-
from rbx import annotations, grading_utils, metadata
|
6
|
-
from rbx.config import get_config
|
7
|
-
from rbx.console import stderr_console
|
8
|
-
from rbx.grading import steps
|
9
|
-
from rbx.grading.judge.sandboxes import stupid_sandbox
|
10
|
-
|
11
|
-
|
12
|
-
def main(
|
13
|
-
problem: annotations.Problem,
|
14
|
-
language: annotations.LanguageWithDefault = None,
|
15
|
-
keep_sandbox: bool = False,
|
16
|
-
):
|
17
|
-
dumped_problem = metadata.find_problem_by_anything(problem)
|
18
|
-
if not dumped_problem:
|
19
|
-
stderr_console.print(
|
20
|
-
f'[error]Problem with identifier [item]{problem}[/item] not found.[/error]'
|
21
|
-
)
|
22
|
-
return
|
23
|
-
|
24
|
-
lang = get_config().get_language(language)
|
25
|
-
if not lang:
|
26
|
-
stderr_console.print(
|
27
|
-
f'[error]Language {language or get_config().defaultLanguage} not found in config. Please check your configuration.[/error]'
|
28
|
-
)
|
29
|
-
return
|
30
|
-
|
31
|
-
box = stupid_sandbox.StupidSandbox()
|
32
|
-
atexit.register(lambda: box.cleanup(delete=not keep_sandbox))
|
33
|
-
|
34
|
-
preprocess_cmds = grading_utils.build_preprocess_commands(dumped_problem, lang)
|
35
|
-
sandbox_params = grading_utils.build_preprocess_sandbox_params()
|
36
|
-
artifacts = grading_utils.build_compile_grading_artifacts(dumped_problem, lang)
|
37
|
-
|
38
|
-
if not steps.compile(preprocess_cmds, sandbox_params, box, artifacts):
|
39
|
-
stderr_console.print(
|
40
|
-
f'[error]Failed to preprocess problem [item]{dumped_problem.pretty_name()}[/item].[/error]'
|
41
|
-
)
|
42
|
-
return
|
43
|
-
|
44
|
-
cmd = shlex.split(lang.exec)
|
45
|
-
os.execv(cmd[0], cmd)
|
rbx/schema.py
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
import uuid
|
2
|
-
from typing import Dict, List, Optional
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
5
|
-
|
6
|
-
from rbx import utils
|
7
|
-
|
8
|
-
|
9
|
-
class Testcase(BaseModel):
|
10
|
-
input: str
|
11
|
-
output: str
|
12
|
-
|
13
|
-
|
14
|
-
class Batch(BaseModel):
|
15
|
-
id: str
|
16
|
-
size: int
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def create():
|
20
|
-
return Batch(id=str(uuid.uuid4()), size=1)
|
21
|
-
|
22
|
-
|
23
|
-
class Problem(BaseModel):
|
24
|
-
name: str
|
25
|
-
group: str = ''
|
26
|
-
url: str = ''
|
27
|
-
interactive: bool = False
|
28
|
-
memoryLimit: int
|
29
|
-
timeLimit: int
|
30
|
-
tests: List[Testcase] = []
|
31
|
-
testType: str = 'single'
|
32
|
-
batch: Batch
|
33
|
-
|
34
|
-
def get_code(self):
|
35
|
-
return self.get_normalized_name()
|
36
|
-
|
37
|
-
def get_normalized_name(self) -> str:
|
38
|
-
return utils.normalize_with_underscores(self.name)
|
39
|
-
|
40
|
-
|
41
|
-
class DumpedProblem(Problem):
|
42
|
-
code: str
|
43
|
-
aliases: List[str]
|
44
|
-
checker: Optional[str] = None
|
45
|
-
|
46
|
-
@staticmethod
|
47
|
-
def from_problem(problem: Problem, **kwargs) -> 'DumpedProblem':
|
48
|
-
return DumpedProblem(**problem.model_dump(), **kwargs)
|
49
|
-
|
50
|
-
def pretty_name(self) -> str:
|
51
|
-
if self.name == self.code:
|
52
|
-
return self.name
|
53
|
-
return f'{self.name} ({self.code})'
|
54
|
-
|
55
|
-
def get_vars(self) -> Dict[str, str]:
|
56
|
-
return {
|
57
|
-
'problem_name': self.name,
|
58
|
-
'problem_code': self.code,
|
59
|
-
'problem_url': self.url,
|
60
|
-
'problem_contest': self.group,
|
61
|
-
'problem_time_limit': f'{self.timeLimit}ms',
|
62
|
-
'problem_memory_limit': f'{self.memoryLimit}MB',
|
63
|
-
'problem_test_type': self.testType,
|
64
|
-
}
|
rbx/submit.py
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
import atexit
|
2
|
-
from pathlib import PosixPath
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
from rbx import annotations, grading_utils, metadata, submitors, utils
|
6
|
-
from rbx.config import get_config
|
7
|
-
from rbx.console import console
|
8
|
-
from rbx.grading import steps
|
9
|
-
from rbx.grading.judge.sandboxes import stupid_sandbox
|
10
|
-
|
11
|
-
|
12
|
-
def main(
|
13
|
-
problem: annotations.Problem,
|
14
|
-
language: Optional[annotations.LanguageWithDefault] = None,
|
15
|
-
keep_sandbox: bool = False,
|
16
|
-
):
|
17
|
-
dumped_problem = metadata.find_problem_by_anything(problem)
|
18
|
-
if not dumped_problem:
|
19
|
-
console.print(
|
20
|
-
f'[error]Problem with identifier [item]{problem}[/item] not found.[/error]'
|
21
|
-
)
|
22
|
-
return
|
23
|
-
|
24
|
-
lang = get_config().get_language(language)
|
25
|
-
if not lang:
|
26
|
-
console.print(
|
27
|
-
f'[error]Language {language or get_config().defaultLanguage} not found in config. Please check your configuration.[/error]'
|
28
|
-
)
|
29
|
-
return
|
30
|
-
|
31
|
-
box = stupid_sandbox.StupidSandbox()
|
32
|
-
atexit.register(lambda: box.cleanup(delete=not keep_sandbox))
|
33
|
-
|
34
|
-
with console.status(
|
35
|
-
f'Preprocessing problem [item]{dumped_problem.pretty_name()}[/item]...'
|
36
|
-
):
|
37
|
-
preprocess_cmds = grading_utils.build_preprocess_commands(dumped_problem, lang)
|
38
|
-
sandbox_params = grading_utils.build_preprocess_sandbox_params()
|
39
|
-
artifacts = grading_utils.build_compile_grading_artifacts(dumped_problem, lang)
|
40
|
-
if not steps.compile(preprocess_cmds, sandbox_params, box, artifacts):
|
41
|
-
console.print(
|
42
|
-
f'[error]Failed to preprocess problem [item]{dumped_problem.pretty_name()}[/item].[/error]'
|
43
|
-
)
|
44
|
-
return
|
45
|
-
|
46
|
-
submit_file = PosixPath(lang.get_submit_file(dumped_problem.code))
|
47
|
-
console.print(
|
48
|
-
f'Problem to be submitted: [item]{dumped_problem.pretty_name()}[/item]'
|
49
|
-
)
|
50
|
-
console.print(f'Submission file: {submit_file.absolute()}')
|
51
|
-
|
52
|
-
if not utils.confirm_on_status(
|
53
|
-
None, 'Do you want to submit this problem?', default=False
|
54
|
-
):
|
55
|
-
console.print('Skipping submission.')
|
56
|
-
return
|
57
|
-
|
58
|
-
if submitors.handle_submit(submit_file, dumped_problem, lang):
|
59
|
-
console.print('[green]Submission successful.[/green]')
|
60
|
-
else:
|
61
|
-
console.print('[error]Submission failed.[/error]')
|