rbx.cp 0.5.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/__init__.py +0 -0
- rbx/annotations.py +127 -0
- rbx/autoenum.py +333 -0
- rbx/box/__init__.py +0 -0
- rbx/box/builder.py +77 -0
- rbx/box/cd.py +37 -0
- rbx/box/checkers.py +134 -0
- rbx/box/code.py +185 -0
- rbx/box/compile.py +56 -0
- rbx/box/conftest.py +42 -0
- rbx/box/contest/__init__.py +0 -0
- rbx/box/contest/build_contest_statements.py +347 -0
- rbx/box/contest/contest_package.py +76 -0
- rbx/box/contest/contest_utils.py +20 -0
- rbx/box/contest/main.py +179 -0
- rbx/box/contest/schema.py +155 -0
- rbx/box/contest/statements.py +82 -0
- rbx/box/creation.py +72 -0
- rbx/box/download.py +64 -0
- rbx/box/environment.py +345 -0
- rbx/box/extensions.py +26 -0
- rbx/box/generators.py +478 -0
- rbx/box/generators_test.py +63 -0
- rbx/box/main.py +449 -0
- rbx/box/package.py +316 -0
- rbx/box/packaging/boca/extension.py +27 -0
- rbx/box/packaging/boca/packager.py +245 -0
- rbx/box/packaging/contest_main.py +82 -0
- rbx/box/packaging/main.py +68 -0
- rbx/box/packaging/packager.py +117 -0
- rbx/box/packaging/polygon/packager.py +320 -0
- rbx/box/packaging/polygon/test.py +81 -0
- rbx/box/packaging/polygon/xml_schema.py +106 -0
- rbx/box/presets/__init__.py +503 -0
- rbx/box/presets/fetch.py +70 -0
- rbx/box/presets/lock_schema.py +20 -0
- rbx/box/presets/schema.py +59 -0
- rbx/box/schema.py +394 -0
- rbx/box/solutions.py +792 -0
- rbx/box/solutions_test.py +41 -0
- rbx/box/statements/__init__.py +0 -0
- rbx/box/statements/build_statements.py +359 -0
- rbx/box/statements/builders.py +375 -0
- rbx/box/statements/joiners.py +113 -0
- rbx/box/statements/latex.py +47 -0
- rbx/box/statements/latex_jinja.py +214 -0
- rbx/box/statements/schema.py +138 -0
- rbx/box/stresses.py +292 -0
- rbx/box/stressing/__init__.py +0 -0
- rbx/box/stressing/finder_parser.py +359 -0
- rbx/box/stressing/generator_parser.py +258 -0
- rbx/box/testcases.py +54 -0
- rbx/box/ui/__init__.py +0 -0
- rbx/box/ui/captured_log.py +372 -0
- rbx/box/ui/css/app.tcss +48 -0
- rbx/box/ui/main.py +38 -0
- rbx/box/ui/run.py +209 -0
- rbx/box/validators.py +245 -0
- rbx/box/validators_test.py +15 -0
- rbx/checker.py +128 -0
- rbx/clone.py +197 -0
- rbx/config.py +271 -0
- rbx/conftest.py +38 -0
- rbx/console.py +27 -0
- rbx/create.py +37 -0
- rbx/edit.py +24 -0
- rbx/grading/__init__.py +0 -0
- rbx/grading/caching.py +356 -0
- rbx/grading/conftest.py +33 -0
- rbx/grading/judge/__init__.py +0 -0
- rbx/grading/judge/cacher.py +503 -0
- rbx/grading/judge/digester.py +35 -0
- rbx/grading/judge/sandbox.py +748 -0
- rbx/grading/judge/sandboxes/__init__.py +0 -0
- rbx/grading/judge/sandboxes/isolate.py +683 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
- rbx/grading/judge/sandboxes/timeit.py +217 -0
- rbx/grading/judge/storage.py +284 -0
- rbx/grading/judge/test.py +38 -0
- rbx/grading/judge/testiso.py +54 -0
- rbx/grading/steps.py +522 -0
- rbx/grading/steps_with_caching.py +59 -0
- rbx/grading/steps_with_caching_run_test.py +429 -0
- rbx/grading_utils.py +148 -0
- rbx/hydration.py +101 -0
- rbx/main.py +122 -0
- rbx/metadata.py +105 -0
- rbx/providers/__init__.py +43 -0
- rbx/providers/codeforces.py +73 -0
- rbx/providers/provider.py +26 -0
- rbx/resources/checkers/boilerplate.cpp +20 -0
- rbx/resources/default_config.json +48 -0
- rbx/resources/envs/default.rbx.yml +37 -0
- rbx/resources/envs/isolate.rbx.yml +37 -0
- rbx/resources/packagers/boca/checker.sh +43 -0
- rbx/resources/packagers/boca/compare +53 -0
- rbx/resources/packagers/boca/compile/c +172 -0
- rbx/resources/packagers/boca/compile/cc +173 -0
- rbx/resources/packagers/boca/compile/cpp +172 -0
- rbx/resources/packagers/boca/compile/java +194 -0
- rbx/resources/packagers/boca/compile/kt +155 -0
- rbx/resources/packagers/boca/compile/pas +172 -0
- rbx/resources/packagers/boca/compile/py2 +173 -0
- rbx/resources/packagers/boca/compile/py3 +173 -0
- rbx/resources/packagers/boca/run/c +128 -0
- rbx/resources/packagers/boca/run/cc +128 -0
- rbx/resources/packagers/boca/run/cpp +128 -0
- rbx/resources/packagers/boca/run/java +194 -0
- rbx/resources/packagers/boca/run/kt +159 -0
- rbx/resources/packagers/boca/run/py2 +166 -0
- rbx/resources/packagers/boca/run/py3 +166 -0
- rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
- rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
- rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
- rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
- rbx/resources/presets/default/preset.rbx.yml +12 -0
- rbx/resources/presets/default/problem/.gitignore +6 -0
- rbx/resources/presets/default/problem/gen.cpp +9 -0
- rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
- rbx/resources/presets/default/problem/random.py +3 -0
- rbx/resources/presets/default/problem/random.txt +2 -0
- rbx/resources/presets/default/problem/sols/main.cpp +9 -0
- rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
- rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
- rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
- rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
- rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
- rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
- rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
- rbx/resources/presets/default/problem/validator.cpp +16 -0
- rbx/resources/presets/default/problem/wcmp.cpp +34 -0
- rbx/resources/templates/template.cpp +19 -0
- rbx/run.py +45 -0
- rbx/schema.py +64 -0
- rbx/submit.py +61 -0
- rbx/submitors/__init__.py +18 -0
- rbx/submitors/codeforces.py +120 -0
- rbx/submitors/submitor.py +25 -0
- rbx/test.py +347 -0
- rbx/testcase.py +70 -0
- rbx/testcase_rendering.py +79 -0
- rbx/testdata/box1/gen1.cpp +7 -0
- rbx/testdata/box1/gen2.cpp +9 -0
- rbx/testdata/box1/genScript.py +2 -0
- rbx/testdata/box1/hard-tle.sol.cpp +26 -0
- rbx/testdata/box1/ole.cpp +17 -0
- rbx/testdata/box1/problem.rbx.yml +39 -0
- rbx/testdata/box1/re.sol.cpp +23 -0
- rbx/testdata/box1/sol.cpp +22 -0
- rbx/testdata/box1/tests/1.in +1 -0
- rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
- rbx/testdata/box1/tle.sol.cpp +35 -0
- rbx/testdata/box1/validator.cpp +11 -0
- rbx/testdata/box1/wa.sol.cpp +22 -0
- rbx/testdata/caching/executable.py +1 -0
- rbx/testdata/compatible +0 -0
- rbx/testing_utils.py +65 -0
- rbx/utils.py +162 -0
- rbx_cp-0.5.0.dist-info/LICENSE +201 -0
- rbx_cp-0.5.0.dist-info/METADATA +89 -0
- rbx_cp-0.5.0.dist-info/RECORD +164 -0
- rbx_cp-0.5.0.dist-info/WHEEL +4 -0
- rbx_cp-0.5.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
import pathlib
|
2
|
+
from typing import Dict, List, Optional
|
3
|
+
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field
|
5
|
+
|
6
|
+
from rbx.box.schema import NameField, Primitive, expand_var
|
7
|
+
from rbx.box.statements.schema import (
|
8
|
+
ConversionStep,
|
9
|
+
Joiner,
|
10
|
+
StatementType,
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
def ShortNameField(**kwargs):
|
15
|
+
return Field(pattern=r'^[A-Z]+[0-9]*$', min_length=1, max_length=4, **kwargs)
|
16
|
+
|
17
|
+
|
18
|
+
class ProblemStatementOverride(BaseModel):
|
19
|
+
model_config = ConfigDict(extra='forbid')
|
20
|
+
|
21
|
+
configure: List[ConversionStep] = Field(
|
22
|
+
[],
|
23
|
+
discriminator='type',
|
24
|
+
description="""
|
25
|
+
Configure how certain conversion steps should happen when applied to the statement file.
|
26
|
+
|
27
|
+
Different from the `steps` field, this does not force the steps to happen, but rather only
|
28
|
+
configure them in case they are applied.
|
29
|
+
""",
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
class ContestStatement(BaseModel):
|
34
|
+
model_config = ConfigDict(extra='forbid')
|
35
|
+
|
36
|
+
language: str = Field('en', description='Language code for this statement.')
|
37
|
+
|
38
|
+
title: str = Field(description='Title of the contest in this language.')
|
39
|
+
|
40
|
+
location: Optional[str] = Field(
|
41
|
+
default=None, description='Location of the contest in this language.'
|
42
|
+
)
|
43
|
+
|
44
|
+
date: Optional[str] = Field(
|
45
|
+
default=None, description='Date of the contest in this language.'
|
46
|
+
)
|
47
|
+
|
48
|
+
path: pathlib.Path = Field(description='Path to the input statement file.')
|
49
|
+
|
50
|
+
type: StatementType = Field(description='Type of the input statement file.')
|
51
|
+
|
52
|
+
joiner: Optional[Joiner] = Field(
|
53
|
+
None,
|
54
|
+
description="""
|
55
|
+
Joiner to be used to build the statement.
|
56
|
+
|
57
|
+
This determines how problem statements will be joined into a single contest statement.""",
|
58
|
+
)
|
59
|
+
|
60
|
+
steps: List[ConversionStep] = Field(
|
61
|
+
default=[],
|
62
|
+
discriminator='type',
|
63
|
+
description="""
|
64
|
+
Describes a sequence of conversion steps that should be applied to the statement file
|
65
|
+
of this contest.
|
66
|
+
|
67
|
+
Usually, it is not necessary to specify these, as they can be inferred from the
|
68
|
+
input statement type and the output statement type, but you can use this to force
|
69
|
+
certain conversion steps to happen.
|
70
|
+
""",
|
71
|
+
)
|
72
|
+
|
73
|
+
configure: List[ConversionStep] = Field(
|
74
|
+
default=[],
|
75
|
+
discriminator='type',
|
76
|
+
description="""
|
77
|
+
Configure how certain conversion steps should happen when applied to the statement file of
|
78
|
+
this contest.
|
79
|
+
|
80
|
+
Different from the `steps` field, this does not force the steps to happen, but rather only
|
81
|
+
configure them in case they are applied.
|
82
|
+
""",
|
83
|
+
)
|
84
|
+
|
85
|
+
assets: List[str] = Field(
|
86
|
+
default=[],
|
87
|
+
description="""
|
88
|
+
Assets relative to the contest directory that should be included while building
|
89
|
+
the statement. Files will be included in the same folder as the statement file.
|
90
|
+
Can be glob pattern as well, such as `imgs/*.png`.
|
91
|
+
""",
|
92
|
+
)
|
93
|
+
|
94
|
+
override: Optional[ProblemStatementOverride] = Field(
|
95
|
+
default=None, description='Override configuration for problem statements.'
|
96
|
+
)
|
97
|
+
|
98
|
+
# Vars to be re-used in the statement.
|
99
|
+
# - It will be available as \VAR{vars} variable in the contest-level box statement.
|
100
|
+
vars: Dict[str, Primitive] = Field(
|
101
|
+
{}, description='Variables to be re-used across the package.'
|
102
|
+
)
|
103
|
+
|
104
|
+
@property
|
105
|
+
def expanded_vars(self) -> Dict[str, Primitive]:
|
106
|
+
return {key: expand_var(value) for key, value in self.vars.items()}
|
107
|
+
|
108
|
+
|
109
|
+
class ContestProblem(BaseModel):
|
110
|
+
short_name: str = ShortNameField(
|
111
|
+
description="""
|
112
|
+
Short name of the problem. Usually, just an uppercase letter,
|
113
|
+
but can be a sequence of uppercase letters followed by a number."""
|
114
|
+
)
|
115
|
+
path: Optional[pathlib.Path] = Field(
|
116
|
+
default=None,
|
117
|
+
description="""
|
118
|
+
Path to the problem relative to the contest package directory.
|
119
|
+
If not specified, will expect the problem to be in ./{short_name}/ folder.""",
|
120
|
+
)
|
121
|
+
|
122
|
+
color: Optional[str] = Field(
|
123
|
+
default=None,
|
124
|
+
description="""Hex-based color that represents this problem in the contest.""",
|
125
|
+
pattern=r'^[A-Za-z0-9]+$',
|
126
|
+
max_length=6,
|
127
|
+
)
|
128
|
+
|
129
|
+
def get_path(self) -> pathlib.Path:
|
130
|
+
return self.path or pathlib.Path(self.short_name)
|
131
|
+
|
132
|
+
|
133
|
+
class Contest(BaseModel):
|
134
|
+
model_config = ConfigDict(extra='forbid')
|
135
|
+
|
136
|
+
name: str = NameField(description='Name of this contest.')
|
137
|
+
|
138
|
+
problems: List[ContestProblem] = Field(
|
139
|
+
default=[], description='List of problems in this contest.'
|
140
|
+
)
|
141
|
+
|
142
|
+
statements: List[ContestStatement] = Field(
|
143
|
+
default=None,
|
144
|
+
description='Configure statements in this contest, per language.',
|
145
|
+
)
|
146
|
+
|
147
|
+
# Vars to be re-used in the statements.
|
148
|
+
# - It will be available as \VAR{vars} variable in the contest-level box statement.
|
149
|
+
vars: Dict[str, Primitive] = Field(
|
150
|
+
{}, description='Variables to be re-used across the package.'
|
151
|
+
)
|
152
|
+
|
153
|
+
@property
|
154
|
+
def expanded_vars(self) -> Dict[str, Primitive]:
|
155
|
+
return {key: expand_var(value) for key, value in self.vars.items()}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from typing import Annotated, List, Optional
|
2
|
+
|
3
|
+
import typer
|
4
|
+
|
5
|
+
from rbx import annotations, console, utils
|
6
|
+
from rbx.box import builder, environment
|
7
|
+
from rbx.box.contest import contest_utils
|
8
|
+
from rbx.box.contest.build_contest_statements import build_statement
|
9
|
+
from rbx.box.contest.contest_package import (
|
10
|
+
find_contest_package_or_die,
|
11
|
+
within_contest,
|
12
|
+
)
|
13
|
+
from rbx.box.statements.schema import StatementType
|
14
|
+
|
15
|
+
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
16
|
+
|
17
|
+
|
18
|
+
@app.command('build, b', help='Build statements.')
|
19
|
+
@within_contest
|
20
|
+
def build(
|
21
|
+
verification: environment.VerificationParam,
|
22
|
+
languages: Annotated[
|
23
|
+
Optional[List[str]],
|
24
|
+
typer.Option(
|
25
|
+
default_factory=list,
|
26
|
+
help='Languages to build statements for. If not specified, build statements for all available languages.',
|
27
|
+
),
|
28
|
+
],
|
29
|
+
output: Annotated[
|
30
|
+
Optional[StatementType],
|
31
|
+
typer.Option(
|
32
|
+
case_sensitive=False,
|
33
|
+
help='Output type to be generated. If not specified, will infer from the conversion steps specified in the package.',
|
34
|
+
),
|
35
|
+
] = StatementType.PDF,
|
36
|
+
samples: Annotated[
|
37
|
+
bool,
|
38
|
+
typer.Option(help='Whether to build the statement with samples or not.'),
|
39
|
+
] = True,
|
40
|
+
editorial: Annotated[
|
41
|
+
bool,
|
42
|
+
typer.Option(help='Whether to add editorial blocks to the statements or not.'),
|
43
|
+
] = False,
|
44
|
+
):
|
45
|
+
contest = find_contest_package_or_die()
|
46
|
+
# At most run the validators, only in samples.
|
47
|
+
if samples:
|
48
|
+
for problem in contest.problems:
|
49
|
+
console.console.print(
|
50
|
+
f'Processing problem [item]{problem.short_name}[/item]...'
|
51
|
+
)
|
52
|
+
with utils.new_cd(problem.get_path()):
|
53
|
+
contest_utils.clear_package_cache()
|
54
|
+
builder.build(verification=verification, groups=set(['samples']))
|
55
|
+
|
56
|
+
contest = find_contest_package_or_die()
|
57
|
+
candidate_languages = languages
|
58
|
+
if not candidate_languages:
|
59
|
+
candidate_languages = sorted(set([st.language for st in contest.statements]))
|
60
|
+
|
61
|
+
for language in candidate_languages:
|
62
|
+
candidates_for_lang = [
|
63
|
+
st for st in contest.statements if st.language == language
|
64
|
+
]
|
65
|
+
if not candidates_for_lang:
|
66
|
+
console.console.print(
|
67
|
+
f'[error]No contest-level statement found for language [item]{language}[/item].[/error]',
|
68
|
+
)
|
69
|
+
raise typer.Exit(1)
|
70
|
+
|
71
|
+
build_statement(
|
72
|
+
candidates_for_lang[0],
|
73
|
+
contest,
|
74
|
+
output_type=output,
|
75
|
+
use_samples=samples,
|
76
|
+
is_editorial=editorial,
|
77
|
+
)
|
78
|
+
|
79
|
+
|
80
|
+
@app.callback()
|
81
|
+
def callback():
|
82
|
+
pass
|
rbx/box/creation.py
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import pathlib
|
2
|
+
import shutil
|
3
|
+
from typing import Annotated, Optional
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from rbx import console
|
8
|
+
from rbx.box import presets
|
9
|
+
from rbx.box.presets.fetch import get_preset_fetch_info
|
10
|
+
|
11
|
+
|
12
|
+
def create(
|
13
|
+
name: Annotated[
|
14
|
+
str,
|
15
|
+
typer.Argument(
|
16
|
+
help='The name of the problem package to create. This will also be the name of the folder.'
|
17
|
+
),
|
18
|
+
],
|
19
|
+
preset: Annotated[
|
20
|
+
Optional[str],
|
21
|
+
typer.Option(
|
22
|
+
'--preset',
|
23
|
+
'-p',
|
24
|
+
help='Which preset to use to create this package. Can be a named of an already installed preset, or an URI, in which case the preset will be downloaded.',
|
25
|
+
),
|
26
|
+
] = None,
|
27
|
+
path: Optional[pathlib.Path] = None,
|
28
|
+
):
|
29
|
+
preset = preset or 'default'
|
30
|
+
console.console.print(f'Creating new problem [item]{name}[/item]...')
|
31
|
+
|
32
|
+
fetch_info = get_preset_fetch_info(preset)
|
33
|
+
if fetch_info is None:
|
34
|
+
console.console.print(
|
35
|
+
f'[error]Invalid preset name/URI [item]{preset}[/item].[/error]'
|
36
|
+
)
|
37
|
+
raise typer.Exit(1)
|
38
|
+
|
39
|
+
if fetch_info.fetch_uri is not None:
|
40
|
+
preset = presets.install_from_remote(fetch_info)
|
41
|
+
|
42
|
+
preset_cfg = presets.get_installed_preset(preset)
|
43
|
+
|
44
|
+
problem_path = (
|
45
|
+
presets.get_preset_installation_path(preset) / preset_cfg.problem
|
46
|
+
if preset_cfg.problem is not None
|
47
|
+
else presets.get_preset_installation_path('default') / 'problem'
|
48
|
+
)
|
49
|
+
|
50
|
+
if not problem_path.is_dir():
|
51
|
+
console.console.print(
|
52
|
+
f'[error]Problem template [item]{problem_path}[/item] does not exist.[/error]'
|
53
|
+
)
|
54
|
+
raise typer.Exit(1)
|
55
|
+
|
56
|
+
dest_path = path or pathlib.Path(name)
|
57
|
+
|
58
|
+
if dest_path.exists():
|
59
|
+
console.console.print(
|
60
|
+
f'[error]Directory [item]{dest_path}[/item] already exists.[/error]'
|
61
|
+
)
|
62
|
+
raise typer.Exit(1)
|
63
|
+
|
64
|
+
shutil.copytree(str(problem_path), str(dest_path))
|
65
|
+
|
66
|
+
# Remove a few left overs.
|
67
|
+
shutil.rmtree(str(dest_path / 'build'), ignore_errors=True)
|
68
|
+
shutil.rmtree(str(dest_path / '.box'), ignore_errors=True)
|
69
|
+
for lock in dest_path.rglob('.preset-lock.yml'):
|
70
|
+
lock.unlink(missing_ok=True)
|
71
|
+
|
72
|
+
presets.generate_lock(preset, root=dest_path)
|
rbx/box/download.py
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
import pathlib
|
2
|
+
import shutil
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from rbx import annotations, console
|
8
|
+
from rbx.box import package
|
9
|
+
from rbx.box.schema import CodeItem
|
10
|
+
from rbx.config import get_builtin_checker, get_jngen, get_testlib
|
11
|
+
from rbx.grading import steps
|
12
|
+
|
13
|
+
app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
|
14
|
+
|
15
|
+
|
16
|
+
def get_local_artifact(name: str) -> Optional[steps.GradingFileInput]:
|
17
|
+
path = pathlib.Path(name)
|
18
|
+
if path.is_file():
|
19
|
+
return steps.GradingFileInput(src=path, dest=path)
|
20
|
+
return None
|
21
|
+
|
22
|
+
|
23
|
+
def maybe_add_testlib(code: CodeItem, artifacts: steps.GradingArtifacts):
|
24
|
+
# Try to get from compilation files, then from package folder, then from tool.
|
25
|
+
artifact = get_local_artifact('testlib.h') or steps.testlib_grading_input()
|
26
|
+
compilation_files = package.get_compilation_files(code)
|
27
|
+
if any(dest == artifact.dest for _, dest in compilation_files):
|
28
|
+
return
|
29
|
+
artifacts.inputs.append(artifact)
|
30
|
+
|
31
|
+
|
32
|
+
def maybe_add_jngen(code: CodeItem, artifacts: steps.GradingArtifacts):
|
33
|
+
# Try to get from compilation files, then from package folder, then from tool.
|
34
|
+
artifact = get_local_artifact('jngen.h') or steps.jngen_grading_input()
|
35
|
+
compilation_files = package.get_compilation_files(code)
|
36
|
+
if any(dest == artifact.dest for _, dest in compilation_files):
|
37
|
+
return
|
38
|
+
artifacts.inputs.append(artifact)
|
39
|
+
|
40
|
+
|
41
|
+
@app.command('testlib', help='Download testlib.h')
|
42
|
+
@package.within_problem
|
43
|
+
def testlib():
|
44
|
+
shutil.copyfile(get_testlib(), pathlib.Path('testlib.h'))
|
45
|
+
console.console.print('Downloaded [item]testlib.h[/item] into current package.')
|
46
|
+
|
47
|
+
|
48
|
+
@app.command('jngen', help='Download jngen.h')
|
49
|
+
@package.within_problem
|
50
|
+
def jngen():
|
51
|
+
shutil.copyfile(get_jngen(), pathlib.Path('jngen.h'))
|
52
|
+
console.console.print('Downloaded [item]jngen.h[/item] into current package.')
|
53
|
+
|
54
|
+
|
55
|
+
@app.command('checker', help='Download a built-in checker from testlib GH repo.')
|
56
|
+
@package.within_problem
|
57
|
+
def checker(name: str):
|
58
|
+
if not name.endswith('.cpp'):
|
59
|
+
name = f'{name}.cpp'
|
60
|
+
path = get_builtin_checker(name)
|
61
|
+
shutil.copyfile(path, pathlib.Path(name))
|
62
|
+
console.console.print(
|
63
|
+
f'[success]Downloaded [item]{name}[/item] into current package.[/success]'
|
64
|
+
)
|