rbx.cp 0.13.8__py3-none-any.whl → 0.16.0__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/__version__.py +1 -0
- rbx/box/cli.py +74 -70
- rbx/box/code.py +3 -0
- rbx/box/contest/build_contest_statements.py +65 -23
- rbx/box/contest/contest_package.py +8 -1
- rbx/box/contest/main.py +9 -3
- rbx/box/contest/schema.py +17 -13
- rbx/box/contest/statements.py +12 -8
- rbx/box/dump_schemas.py +2 -1
- rbx/box/environment.py +1 -1
- rbx/box/fields.py +22 -4
- rbx/box/generators.py +32 -13
- rbx/box/git_utils.py +29 -1
- rbx/box/limits_info.py +161 -0
- rbx/box/package.py +18 -1
- rbx/box/packaging/boca/boca_language_utils.py +26 -0
- rbx/box/packaging/boca/boca_outcome_utils.py +10 -0
- rbx/box/packaging/boca/packager.py +7 -5
- rbx/box/packaging/contest_main.py +20 -12
- rbx/box/packaging/packager.py +24 -14
- rbx/box/packaging/polygon/packager.py +7 -3
- rbx/box/packaging/polygon/upload.py +2 -1
- rbx/box/presets/__init__.py +143 -78
- rbx/box/presets/fetch.py +10 -2
- rbx/box/presets/schema.py +16 -1
- rbx/box/remote.py +3 -3
- rbx/box/sanitizers/issue_stack.py +124 -0
- rbx/box/schema.py +87 -27
- rbx/box/solutions.py +74 -117
- rbx/box/statements/build_statements.py +12 -1
- rbx/box/statements/builders.py +5 -3
- rbx/box/statements/latex_jinja.py +73 -23
- rbx/box/statements/schema.py +7 -9
- rbx/box/stressing/generator_parser.py +3 -1
- rbx/box/tasks.py +10 -10
- rbx/box/testcase_extractors.py +8 -0
- rbx/box/testing/testing_preset.py +129 -2
- rbx/box/testing/testing_shared.py +3 -1
- rbx/box/timing.py +305 -0
- rbx/box/tooling/boca/debug_utils.py +88 -0
- rbx/box/tooling/boca/manual_scrape.py +20 -0
- rbx/box/tooling/boca/scraper.py +660 -57
- rbx/box/unit.py +0 -2
- rbx/box/validators.py +0 -4
- rbx/grading/judge/cacher.py +36 -0
- rbx/grading/judge/program.py +12 -2
- rbx/grading/judge/sandbox.py +1 -1
- rbx/grading/judge/sandboxes/stupid_sandbox.py +2 -1
- rbx/grading/judge/storage.py +36 -3
- rbx/grading/limits.py +4 -0
- rbx/grading/steps.py +3 -2
- rbx/resources/presets/default/contest/contest.rbx.yml +7 -1
- rbx/resources/presets/default/contest/statement/info.rbx.tex +46 -0
- rbx/resources/presets/default/preset.rbx.yml +1 -0
- rbx/resources/presets/default/problem/.gitignore +1 -0
- rbx/resources/presets/default/problem/problem.rbx.yml +19 -3
- rbx/resources/presets/default/problem/rbx.h +52 -5
- rbx/resources/presets/default/problem/statement/statement.rbx.tex +6 -2
- rbx/resources/presets/default/problem/testlib.h +6299 -0
- rbx/resources/presets/default/problem/validator.cpp +4 -3
- rbx/resources/presets/default/shared/contest_template.rbx.tex +8 -4
- rbx/resources/presets/default/shared/icpc.sty +18 -3
- rbx/resources/presets/default/shared/problem_template.rbx.tex +4 -1
- rbx/testing_utils.py +17 -1
- rbx/utils.py +45 -0
- {rbx_cp-0.13.8.dist-info → rbx_cp-0.16.0.dist-info}/METADATA +5 -2
- {rbx_cp-0.13.8.dist-info → rbx_cp-0.16.0.dist-info}/RECORD +71 -67
- {rbx_cp-0.13.8.dist-info → rbx_cp-0.16.0.dist-info}/entry_points.txt +0 -1
- rbx/providers/__init__.py +0 -43
- rbx/providers/codeforces.py +0 -73
- rbx/providers/provider.py +0 -26
- rbx/submitors/__init__.py +0 -18
- rbx/submitors/codeforces.py +0 -121
- rbx/submitors/submitor.py +0 -25
- /rbx/resources/presets/default/problem/sols/{wa.cpp → wa-overflow.cpp} +0 -0
- {rbx_cp-0.13.8.dist-info → rbx_cp-0.16.0.dist-info}/LICENSE +0 -0
- {rbx_cp-0.13.8.dist-info → rbx_cp-0.16.0.dist-info}/WHEEL +0 -0
rbx/box/generators.py
CHANGED
@@ -378,7 +378,8 @@ async def generate_testcases(
|
|
378
378
|
|
379
379
|
|
380
380
|
async def generate_output_for_testcase(
|
381
|
-
|
381
|
+
model_solution: CodeItem,
|
382
|
+
model_solution_digest: str,
|
382
383
|
testcase: Testcase,
|
383
384
|
interactor_digest: Optional[str] = None,
|
384
385
|
capture_pipes: Optional[bool] = None,
|
@@ -387,13 +388,9 @@ async def generate_output_for_testcase(
|
|
387
388
|
testcase.inputPath.parent.mkdir(parents=True, exist_ok=True)
|
388
389
|
testcase.outputPath.parent.mkdir(parents=True, exist_ok=True)
|
389
390
|
|
390
|
-
main_solution = package.get_main_solution()
|
391
|
-
if main_solution is None:
|
392
|
-
return
|
393
|
-
|
394
391
|
eval: Evaluation = await run_solution_on_testcase(
|
395
|
-
|
396
|
-
|
392
|
+
model_solution,
|
393
|
+
model_solution_digest,
|
397
394
|
None,
|
398
395
|
testcase,
|
399
396
|
interactor_digest=interactor_digest,
|
@@ -439,7 +436,7 @@ async def generate_outputs_for_testcases(
|
|
439
436
|
needs_output = _needs_output(generation_entries)
|
440
437
|
|
441
438
|
main_solution = package.get_main_solution()
|
442
|
-
|
439
|
+
solution_digest_map = {}
|
443
440
|
|
444
441
|
pkg = package.find_problem_package_or_die()
|
445
442
|
|
@@ -452,11 +449,30 @@ async def generate_outputs_for_testcases(
|
|
452
449
|
if progress:
|
453
450
|
progress.update('Compiling main solution...')
|
454
451
|
try:
|
455
|
-
|
452
|
+
solution_digest_map[main_solution.path] = compile_item(main_solution)
|
456
453
|
except:
|
457
454
|
console.console.print('[error]Failed compiling main solution.[/error]')
|
458
455
|
raise
|
459
456
|
|
457
|
+
for entry in generation_entries:
|
458
|
+
if (
|
459
|
+
entry.model_solution is not None
|
460
|
+
and entry.model_solution.path not in solution_digest_map
|
461
|
+
):
|
462
|
+
if progress:
|
463
|
+
progress.update(
|
464
|
+
f'Compiling model solution [item]{entry.model_solution.path}[/item]...'
|
465
|
+
)
|
466
|
+
try:
|
467
|
+
solution_digest_map[entry.model_solution.path] = compile_item(
|
468
|
+
entry.model_solution
|
469
|
+
)
|
470
|
+
except:
|
471
|
+
console.console.print(
|
472
|
+
f'[error]Failed compiling model solution [item]{entry.model_solution.path}[/item].[/error]'
|
473
|
+
)
|
474
|
+
raise
|
475
|
+
|
460
476
|
gen_runs_dir = package.get_problem_runs_dir() / '.gen'
|
461
477
|
shutil.rmtree(str(gen_runs_dir), ignore_errors=True)
|
462
478
|
gen_runs_dir.mkdir(parents=True, exist_ok=True)
|
@@ -476,15 +492,17 @@ async def generate_outputs_for_testcases(
|
|
476
492
|
continue
|
477
493
|
|
478
494
|
assert needs_output
|
495
|
+
model_solution = entry.model_solution or main_solution
|
479
496
|
if (
|
480
|
-
|
497
|
+
model_solution is None or model_solution.path not in solution_digest_map
|
481
498
|
) and not tc.outputPath.is_file():
|
482
499
|
console.console.print(
|
483
|
-
'[error]No main solution found to generate outputs for testcases.[/error]',
|
500
|
+
'[error]No main/model solution found to generate outputs for testcases.[/error]',
|
484
501
|
)
|
485
502
|
raise typer.Exit(1)
|
486
503
|
|
487
|
-
assert
|
504
|
+
assert model_solution is not None
|
505
|
+
model_solution_digest = solution_digest_map[model_solution.path]
|
488
506
|
capture_pipes = None
|
489
507
|
if (
|
490
508
|
pkg.type == TaskType.COMMUNICATION
|
@@ -497,7 +515,8 @@ async def generate_outputs_for_testcases(
|
|
497
515
|
)
|
498
516
|
|
499
517
|
await generate_output_for_testcase(
|
500
|
-
|
518
|
+
model_solution,
|
519
|
+
model_solution_digest,
|
501
520
|
tc,
|
502
521
|
interactor_digest=interactor_digest,
|
503
522
|
capture_pipes=capture_pipes,
|
rbx/box/git_utils.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import pathlib
|
2
|
-
|
2
|
+
import subprocess
|
3
|
+
from typing import List, Optional
|
3
4
|
|
4
5
|
import git
|
6
|
+
import semver
|
5
7
|
|
6
8
|
|
7
9
|
def get_repo_or_nil(
|
@@ -26,3 +28,29 @@ def get_any_remote(repo: git.Repo) -> Optional[git.Remote]:
|
|
26
28
|
if remote.exists():
|
27
29
|
return remote
|
28
30
|
return None
|
31
|
+
|
32
|
+
|
33
|
+
def _parse_tag_from_ref(ref: str) -> str:
|
34
|
+
return ref.split('/')[-1].split('^{}')[0]
|
35
|
+
|
36
|
+
|
37
|
+
def ls_remote_tags(uri: str) -> List[str]:
|
38
|
+
completed_process = subprocess.run(
|
39
|
+
['git', 'ls-remote', '--tags', uri],
|
40
|
+
check=True,
|
41
|
+
capture_output=True,
|
42
|
+
text=True,
|
43
|
+
)
|
44
|
+
return [
|
45
|
+
_parse_tag_from_ref(line.split('\t')[1])
|
46
|
+
for line in completed_process.stdout.split('\n')
|
47
|
+
if line
|
48
|
+
]
|
49
|
+
|
50
|
+
|
51
|
+
def latest_remote_tag(uri: str) -> str:
|
52
|
+
tags = ls_remote_tags(uri)
|
53
|
+
valid_tags = [tag for tag in tags if semver.Version.is_valid(tag)]
|
54
|
+
if not valid_tags:
|
55
|
+
raise ValueError(f'No valid tags found for {uri}')
|
56
|
+
return sorted(valid_tags, key=semver.VersionInfo.parse)[-1]
|
rbx/box/limits_info.py
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
import contextvars
|
2
|
+
import pathlib
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from rbx import console, utils
|
8
|
+
from rbx.box import package
|
9
|
+
from rbx.box.environment import VerificationLevel
|
10
|
+
from rbx.box.schema import LimitsProfile
|
11
|
+
from rbx.grading.limits import Limits
|
12
|
+
|
13
|
+
profile_var = contextvars.ContextVar[Optional[str]]('profile', default=None)
|
14
|
+
|
15
|
+
|
16
|
+
def get_active_profile() -> Optional[str]:
|
17
|
+
return profile_var.get()
|
18
|
+
|
19
|
+
|
20
|
+
class use_profile:
|
21
|
+
def __init__(self, profile: Optional[str]):
|
22
|
+
self.profile = profile
|
23
|
+
self.token = None
|
24
|
+
|
25
|
+
def __enter__(self):
|
26
|
+
self.token = profile_var.set(self.profile)
|
27
|
+
|
28
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
29
|
+
if self.token is not None:
|
30
|
+
profile_var.reset(self.token)
|
31
|
+
|
32
|
+
|
33
|
+
def _expand_limits_profile(
|
34
|
+
limits_profile: LimitsProfile, root: pathlib.Path
|
35
|
+
) -> LimitsProfile:
|
36
|
+
pkg = package.find_problem_package_or_die(root=root)
|
37
|
+
res = LimitsProfile(
|
38
|
+
timeLimit=pkg.timeLimit,
|
39
|
+
memoryLimit=pkg.memoryLimit,
|
40
|
+
outputLimit=pkg.outputLimit,
|
41
|
+
)
|
42
|
+
for language, modifier in pkg.modifiers.items():
|
43
|
+
res.modifiers[language] = modifier.model_copy(deep=True)
|
44
|
+
|
45
|
+
if limits_profile.inheritFromPackage:
|
46
|
+
return res
|
47
|
+
|
48
|
+
time_is_overridden = limits_profile.timeLimit is not None
|
49
|
+
memory_is_overridden = limits_profile.memoryLimit is not None
|
50
|
+
output_is_overridden = limits_profile.outputLimit is not None
|
51
|
+
if time_is_overridden:
|
52
|
+
res.timeLimit = limits_profile.timeLimit
|
53
|
+
if memory_is_overridden:
|
54
|
+
res.memoryLimit = limits_profile.memoryLimit
|
55
|
+
if output_is_overridden:
|
56
|
+
res.outputLimit = limits_profile.outputLimit
|
57
|
+
|
58
|
+
for modifier in res.modifiers.values():
|
59
|
+
# Clean up modifiers coming from the package that are not overridden
|
60
|
+
# by the base limits profile.
|
61
|
+
if time_is_overridden:
|
62
|
+
modifier.time = None
|
63
|
+
if memory_is_overridden:
|
64
|
+
modifier.memory = None
|
65
|
+
|
66
|
+
for language, modifier in limits_profile.modifiers.items():
|
67
|
+
if modifier.time is not None:
|
68
|
+
res.modifiers[language].time = modifier.time
|
69
|
+
if modifier.timeMultiplier is not None:
|
70
|
+
res.modifiers[language].timeMultiplier = modifier.timeMultiplier
|
71
|
+
if modifier.memory is not None:
|
72
|
+
res.modifiers[language].memory = modifier.memory
|
73
|
+
return res
|
74
|
+
|
75
|
+
|
76
|
+
def _get_limits_from_profile(
|
77
|
+
language: Optional[str],
|
78
|
+
limits_profile: LimitsProfile,
|
79
|
+
source_profile: Optional[str],
|
80
|
+
verification: VerificationLevel,
|
81
|
+
root: pathlib.Path,
|
82
|
+
) -> Limits:
|
83
|
+
limits_profile = _expand_limits_profile(limits_profile, root=root)
|
84
|
+
return Limits(
|
85
|
+
time=limits_profile.timelimit_for_language(language),
|
86
|
+
memory=limits_profile.memorylimit_for_language(language),
|
87
|
+
output=limits_profile.outputLimit,
|
88
|
+
isDoubleTL=verification.value >= VerificationLevel.FULL.value,
|
89
|
+
profile=source_profile,
|
90
|
+
)
|
91
|
+
|
92
|
+
|
93
|
+
def get_saved_limits_profile(
|
94
|
+
profile: str = 'local', root: pathlib.Path = pathlib.Path()
|
95
|
+
) -> Optional[LimitsProfile]:
|
96
|
+
limits_path = package.get_limits_file(profile, root=root)
|
97
|
+
if not limits_path.exists():
|
98
|
+
return None
|
99
|
+
return utils.model_from_yaml(LimitsProfile, limits_path.read_text())
|
100
|
+
|
101
|
+
|
102
|
+
def get_package_limits_profile(root: pathlib.Path = pathlib.Path()) -> LimitsProfile:
|
103
|
+
profile = LimitsProfile(inheritFromPackage=True)
|
104
|
+
return _expand_limits_profile(profile, root=root)
|
105
|
+
|
106
|
+
|
107
|
+
def get_package_limits(
|
108
|
+
verification: VerificationLevel = VerificationLevel.NONE,
|
109
|
+
root: pathlib.Path = pathlib.Path(),
|
110
|
+
) -> Limits:
|
111
|
+
return _get_limits_from_profile(
|
112
|
+
language=None,
|
113
|
+
limits_profile=get_package_limits_profile(root=root),
|
114
|
+
source_profile=None,
|
115
|
+
verification=verification,
|
116
|
+
root=root,
|
117
|
+
)
|
118
|
+
|
119
|
+
|
120
|
+
def get_limits_profile(
|
121
|
+
profile: Optional[str] = None,
|
122
|
+
fallback_to_package_profile: bool = True,
|
123
|
+
root: pathlib.Path = pathlib.Path(),
|
124
|
+
) -> LimitsProfile:
|
125
|
+
if profile is None:
|
126
|
+
return get_package_limits_profile(root=root)
|
127
|
+
saved_profile = get_saved_limits_profile(profile, root=root)
|
128
|
+
if saved_profile is None:
|
129
|
+
if fallback_to_package_profile:
|
130
|
+
return get_package_limits_profile(root=root)
|
131
|
+
console.console.print(
|
132
|
+
f'[error]Limits profile [item]{profile}[/item] not found.[/error]'
|
133
|
+
)
|
134
|
+
raise typer.Exit(1)
|
135
|
+
return _expand_limits_profile(saved_profile, root=root)
|
136
|
+
|
137
|
+
|
138
|
+
def get_limits(
|
139
|
+
language: Optional[str] = None,
|
140
|
+
profile: Optional[str] = None,
|
141
|
+
fallback_to_package_profile: bool = True,
|
142
|
+
verification: VerificationLevel = VerificationLevel.NONE,
|
143
|
+
root: pathlib.Path = pathlib.Path(),
|
144
|
+
) -> Limits:
|
145
|
+
source_profile = None
|
146
|
+
limits_profile = LimitsProfile(inheritFromPackage=True)
|
147
|
+
if profile is not None:
|
148
|
+
specified_limits_profile = get_saved_limits_profile(profile, root=root)
|
149
|
+
if specified_limits_profile is not None:
|
150
|
+
limits_profile = specified_limits_profile
|
151
|
+
source_profile = profile
|
152
|
+
elif not fallback_to_package_profile:
|
153
|
+
console.console.print(
|
154
|
+
f'[error]Limits profile [item]{profile}[/item] not found.[/error]'
|
155
|
+
)
|
156
|
+
raise typer.Exit(1)
|
157
|
+
|
158
|
+
res = _get_limits_from_profile(
|
159
|
+
language, limits_profile, source_profile, verification, root=root
|
160
|
+
)
|
161
|
+
return res
|
rbx/box/package.py
CHANGED
@@ -12,6 +12,7 @@ from rbx import console, utils
|
|
12
12
|
from rbx.box import cd, global_package
|
13
13
|
from rbx.box.environment import get_sandbox_type
|
14
14
|
from rbx.box.global_package import get_cache_fingerprint
|
15
|
+
from rbx.box.sanitizers import issue_stack
|
15
16
|
from rbx.box.schema import (
|
16
17
|
CodeItem,
|
17
18
|
ExpectedOutcome,
|
@@ -80,7 +81,13 @@ def within_problem(func):
|
|
80
81
|
@functools.wraps(func)
|
81
82
|
def wrapper(*args, **kwargs):
|
82
83
|
with cd.new_package_cd(find_problem()):
|
83
|
-
|
84
|
+
issue_level_token = issue_stack.issue_level_var.set(
|
85
|
+
issue_stack.IssueLevel.DETAILED
|
86
|
+
)
|
87
|
+
ret = func(*args, **kwargs)
|
88
|
+
issue_stack.print_current_report()
|
89
|
+
issue_stack.issue_level_var.reset(issue_level_token)
|
90
|
+
return ret
|
84
91
|
|
85
92
|
return wrapper
|
86
93
|
|
@@ -147,6 +154,16 @@ def get_problem_iruns_dir(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
|
147
154
|
return iruns_dir
|
148
155
|
|
149
156
|
|
157
|
+
def get_limits_dir(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
158
|
+
limits_dir = root / '.limits'
|
159
|
+
limits_dir.mkdir(parents=True, exist_ok=True)
|
160
|
+
return limits_dir
|
161
|
+
|
162
|
+
|
163
|
+
def get_limits_file(profile: str, root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
164
|
+
return get_limits_dir(root) / f'{profile}.yml'
|
165
|
+
|
166
|
+
|
150
167
|
def get_problem_preprocessed_path(
|
151
168
|
item: pathlib.Path, root: pathlib.Path = pathlib.Path()
|
152
169
|
) -> pathlib.Path:
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import typing
|
2
|
+
|
3
|
+
from rbx.box.environment import get_environment, get_language
|
4
|
+
from rbx.box.packaging.boca.extension import BocaLanguage, BocaLanguageExtension
|
5
|
+
|
6
|
+
|
7
|
+
def get_rbx_language_from_boca_language(boca_language: BocaLanguage) -> str:
|
8
|
+
for language in get_environment().languages:
|
9
|
+
language_extension = language.get_extension_or_default(
|
10
|
+
language.name, BocaLanguageExtension
|
11
|
+
)
|
12
|
+
if language_extension.bocaLanguage == boca_language:
|
13
|
+
return language.name
|
14
|
+
return boca_language
|
15
|
+
|
16
|
+
|
17
|
+
def get_boca_language_from_rbx_language(rbx_language: str) -> BocaLanguage:
|
18
|
+
language = get_language(rbx_language)
|
19
|
+
language_extension = language.get_extension_or_default(
|
20
|
+
'boca', BocaLanguageExtension
|
21
|
+
)
|
22
|
+
if language_extension.bocaLanguage:
|
23
|
+
return typing.cast(BocaLanguage, language_extension.bocaLanguage)
|
24
|
+
if rbx_language.lower() in typing.get_args(BocaLanguage):
|
25
|
+
return typing.cast(BocaLanguage, rbx_language.lower())
|
26
|
+
raise ValueError(f'No Boca language found for Rbx language {rbx_language}')
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from rbx.box.schema import ExpectedOutcome
|
2
|
+
|
3
|
+
|
4
|
+
def simplify_rbx_expected_outcome(outcome: ExpectedOutcome) -> ExpectedOutcome:
|
5
|
+
if outcome in [
|
6
|
+
ExpectedOutcome.OUTPUT_LIMIT_EXCEEDED,
|
7
|
+
ExpectedOutcome.MEMORY_LIMIT_EXCEEDED,
|
8
|
+
]:
|
9
|
+
return ExpectedOutcome.RUNTIME_ERROR
|
10
|
+
return outcome
|
@@ -6,7 +6,7 @@ from typing import List
|
|
6
6
|
import typer
|
7
7
|
|
8
8
|
from rbx import console
|
9
|
-
from rbx.box import header, naming, package
|
9
|
+
from rbx.box import header, limits_info, naming, package
|
10
10
|
from rbx.box.environment import get_extension_or_default
|
11
11
|
from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
|
12
12
|
from rbx.box.packaging.packager import BasePackager, BuiltStatement
|
@@ -71,12 +71,14 @@ class BocaPackager(BasePackager):
|
|
71
71
|
)
|
72
72
|
|
73
73
|
def _get_pkg_timelimit(self, language: BocaLanguage) -> int:
|
74
|
-
|
75
|
-
|
74
|
+
limits = limits_info.get_limits(language, profile='boca')
|
75
|
+
assert limits.time is not None
|
76
|
+
return limits.time
|
76
77
|
|
77
78
|
def _get_pkg_memorylimit(self, language: BocaLanguage) -> int:
|
78
|
-
|
79
|
-
|
79
|
+
limits = limits_info.get_limits(language, profile='boca')
|
80
|
+
assert limits.memory is not None
|
81
|
+
return limits.memory
|
80
82
|
|
81
83
|
def _get_number_of_runs(self, language: BocaLanguage) -> int:
|
82
84
|
pkg = package.find_problem_package_or_die()
|
@@ -6,7 +6,7 @@ import syncer
|
|
6
6
|
import typer
|
7
7
|
|
8
8
|
from rbx import annotations, console
|
9
|
-
from rbx.box import cd, environment, package
|
9
|
+
from rbx.box import cd, environment, limits_info, package
|
10
10
|
from rbx.box.contest import build_contest_statements, contest_package
|
11
11
|
from rbx.box.packaging.packager import (
|
12
12
|
BaseContestPackager,
|
@@ -28,6 +28,11 @@ async def run_contest_packager(
|
|
28
28
|
):
|
29
29
|
contest = contest_package.find_contest_package_or_die()
|
30
30
|
|
31
|
+
if limits_info.get_saved_limits_profile(contest_packager_cls.name()) is not None:
|
32
|
+
console.console.print(
|
33
|
+
f'[warning]Using saved limits profile for [item]{contest_packager_cls.name()}[/item].[/warning]'
|
34
|
+
)
|
35
|
+
|
31
36
|
# Build problem-level packages.
|
32
37
|
built_packages = []
|
33
38
|
for problem in contest.problems:
|
@@ -52,21 +57,24 @@ async def run_contest_packager(
|
|
52
57
|
statement_types = packager.statement_types()
|
53
58
|
built_statements = []
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
60
|
+
with limits_info.use_profile(contest_packager_cls.name()):
|
61
|
+
for statement_type in statement_types:
|
62
|
+
languages = packager.languages()
|
63
|
+
for language in languages:
|
64
|
+
statement = packager.get_statement_for_language(language)
|
65
|
+
statement_path = build_contest_statements.build_statement(
|
66
|
+
statement, contest, statement_type
|
67
|
+
)
|
68
|
+
built_statements.append(
|
69
|
+
BuiltContestStatement(statement, statement_path, statement_type)
|
70
|
+
)
|
65
71
|
|
66
72
|
console.console.print(f'Packaging contest for [item]{packager.name()}[/item]...')
|
67
73
|
|
68
74
|
# Build contest-level package.
|
69
|
-
with tempfile.TemporaryDirectory() as td
|
75
|
+
with tempfile.TemporaryDirectory() as td, limits_info.use_profile(
|
76
|
+
contest_packager_cls.name()
|
77
|
+
):
|
70
78
|
result_path = packager.package(
|
71
79
|
built_packages, pathlib.Path('build'), pathlib.Path(td), built_statements
|
72
80
|
)
|
rbx/box/packaging/packager.py
CHANGED
@@ -8,7 +8,7 @@ from typing import List, Tuple, Type
|
|
8
8
|
import typer
|
9
9
|
|
10
10
|
from rbx import console
|
11
|
-
from rbx.box import environment, header, naming, package
|
11
|
+
from rbx.box import environment, header, limits_info, naming, package
|
12
12
|
from rbx.box.contest import contest_package
|
13
13
|
from rbx.box.contest.schema import ContestProblem, ContestStatement
|
14
14
|
from rbx.box.formatting import href
|
@@ -103,8 +103,9 @@ class BasePackager(ABC):
|
|
103
103
|
|
104
104
|
|
105
105
|
class BaseContestPackager(ABC):
|
106
|
+
@classmethod
|
106
107
|
@abstractmethod
|
107
|
-
def name(
|
108
|
+
def name(cls) -> str:
|
108
109
|
pass
|
109
110
|
|
110
111
|
@abstractmethod
|
@@ -180,11 +181,17 @@ async def run_packager(
|
|
180
181
|
|
181
182
|
header.generate_header()
|
182
183
|
|
183
|
-
if
|
184
|
+
if limits_info.get_saved_limits_profile(packager_cls.name()) is not None:
|
184
185
|
console.console.print(
|
185
|
-
'[
|
186
|
+
f'[warning]Using saved limits profile for [item]{packager_cls.name()}[/item].[/warning]'
|
186
187
|
)
|
187
|
-
|
188
|
+
|
189
|
+
with limits_info.use_profile(packager_cls.name()):
|
190
|
+
if not await builder.verify(verification=verification):
|
191
|
+
console.console.print(
|
192
|
+
'[error]Build or verification failed, check the report.[/error]'
|
193
|
+
)
|
194
|
+
raise typer.Exit(1)
|
188
195
|
|
189
196
|
pkg = package.find_problem_package_or_die()
|
190
197
|
|
@@ -199,18 +206,21 @@ async def run_packager(
|
|
199
206
|
statement_types = packager.statement_types()
|
200
207
|
built_statements = []
|
201
208
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
209
|
+
with limits_info.use_profile(packager_cls.name()):
|
210
|
+
for statement_type in statement_types:
|
211
|
+
languages = packager.languages()
|
212
|
+
for language in languages:
|
213
|
+
statement = packager.get_statement_for_language(language)
|
214
|
+
statement_path = build_statement(statement, pkg, statement_type)
|
215
|
+
built_statements.append(
|
216
|
+
BuiltStatement(statement, statement_path, statement_type)
|
217
|
+
)
|
210
218
|
|
211
219
|
console.console.print(f'Packaging problem for [item]{packager.name()}[/item]...')
|
212
220
|
|
213
|
-
with tempfile.TemporaryDirectory() as td
|
221
|
+
with tempfile.TemporaryDirectory() as td, limits_info.use_profile(
|
222
|
+
packager_cls.name()
|
223
|
+
):
|
214
224
|
result_path = packager.package(
|
215
225
|
package.get_build_path(), pathlib.Path(td), built_statements
|
216
226
|
)
|
@@ -5,7 +5,7 @@ from typing import List, Optional
|
|
5
5
|
import typer
|
6
6
|
|
7
7
|
from rbx import console, utils
|
8
|
-
from rbx.box import header, package
|
8
|
+
from rbx.box import header, limits_info, package
|
9
9
|
from rbx.box.lang import code_to_lang, code_to_langs, is_valid_lang_code
|
10
10
|
from rbx.box.packaging.packager import (
|
11
11
|
BaseContestPackager,
|
@@ -123,10 +123,14 @@ class PolygonPackager(BasePackager):
|
|
123
123
|
|
124
124
|
testcases = self.get_flattened_built_testcases()
|
125
125
|
|
126
|
+
limits = limits_info.get_limits(None, profile='polygon')
|
127
|
+
assert limits.time is not None
|
128
|
+
assert limits.memory is not None
|
129
|
+
|
126
130
|
return polygon_schema.Testset(
|
127
131
|
name='tests',
|
128
|
-
timelimit=
|
129
|
-
memorylimit=
|
132
|
+
timelimit=limits.time,
|
133
|
+
memorylimit=limits.memory * 1024 * 1024,
|
130
134
|
size=len(testcases),
|
131
135
|
inputPattern='tests/%03d',
|
132
136
|
answerPattern='tests/%03d.a',
|
@@ -8,7 +8,7 @@ import rich.progress
|
|
8
8
|
import typer
|
9
9
|
|
10
10
|
from rbx import console
|
11
|
-
from rbx.box import header, package
|
11
|
+
from rbx.box import header, limits_info, package
|
12
12
|
from rbx.box.generators import get_all_built_testcases
|
13
13
|
from rbx.box.lang import code_to_langs, is_valid_lang_code
|
14
14
|
from rbx.box.packaging.polygon import polygon_api as api
|
@@ -250,6 +250,7 @@ def _get_statement_blocks(statement: Statement) -> StatementBlocks:
|
|
250
250
|
pkg = package.find_problem_package_or_die()
|
251
251
|
# TODO: pull this from a library, too hacky at the moment
|
252
252
|
builder_problem = StatementBuilderProblem(
|
253
|
+
limits=limits_info.get_limits_profile(profile='polygon'),
|
253
254
|
package=pkg,
|
254
255
|
statement=statement,
|
255
256
|
vars={
|