rbx.cp 0.5.50__py3-none-any.whl → 0.5.52__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/box/cli.py CHANGED
@@ -27,6 +27,7 @@ from rbx.box import (
27
27
  from rbx.box.contest import main as contest
28
28
  from rbx.box.contest.contest_package import find_contest_yaml
29
29
  from rbx.box.environment import VerificationLevel, get_environment_path
30
+ from rbx.box.header import generate_header
30
31
  from rbx.box.packaging import main as packaging
31
32
  from rbx.box.schema import CodeItem, ExpectedOutcome, TestcaseGroup
32
33
  from rbx.box.solutions import (
@@ -121,10 +122,12 @@ def main(
121
122
  state.STATE.debug_logs = debug_logs
122
123
 
123
124
 
124
- # @app.command('ui', hidden=True)
125
- # @package.within_problem
126
- # def ui():
127
- # ui_pkg.start()
125
+ @app.command('ui', hidden=True)
126
+ @package.within_problem
127
+ def ui():
128
+ from rbx.box.ui import main as ui_pkg
129
+
130
+ ui_pkg.start()
128
131
 
129
132
 
130
133
  @app.command(
@@ -755,6 +758,16 @@ def unit_tests():
755
758
  unit.run_unit_tests(s)
756
759
 
757
760
 
761
+ @app.command(
762
+ 'header',
763
+ rich_help_panel='Configuration',
764
+ help='Generate the rbx.h header file.',
765
+ )
766
+ @package.within_problem
767
+ def header():
768
+ generate_header()
769
+
770
+
758
771
  @app.command(
759
772
  'environment, env',
760
773
  rich_help_panel='Configuration',
rbx/box/code.py CHANGED
@@ -193,7 +193,7 @@ def _check_stack_limit():
193
193
  ```
194
194
  function rbx() {{
195
195
  local rbx_bin=`bash -c "type -P rbx"`
196
- ulimit -s {target_text // 1024} && $rbx_bin $@
196
+ ulimit -s {target_text // 1024} && $rbx_bin "$@"
197
197
  }}
198
198
  ```
199
199
  """
@@ -415,6 +415,7 @@ def compile_item(
415
415
 
416
416
  download.maybe_add_testlib(code, artifacts)
417
417
  download.maybe_add_jngen(code, artifacts)
418
+ download.maybe_add_rbx_header(code, artifacts)
418
419
  artifacts.inputs.append(
419
420
  GradingFileInput(src=generator_path, dest=PosixPath(file_mapping.compilable))
420
421
  )
rbx/box/download.py CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
5
5
  import typer
6
6
 
7
7
  from rbx import annotations, console
8
- from rbx.box import package
8
+ from rbx.box import header, package
9
9
  from rbx.box.schema import CodeItem
10
10
  from rbx.config import get_builtin_checker, get_jngen, get_testlib
11
11
  from rbx.grading import steps
@@ -20,6 +20,16 @@ def get_local_artifact(name: str) -> Optional[steps.GradingFileInput]:
20
20
  return None
21
21
 
22
22
 
23
+ def maybe_add_rbx_header(code: CodeItem, artifacts: steps.GradingArtifacts):
24
+ header.get_header()
25
+ artifact = get_local_artifact('rbx.h')
26
+ assert artifact is not None
27
+ compilation_files = package.get_compilation_files(code)
28
+ if any(dest == artifact.dest for _, dest in compilation_files):
29
+ return
30
+ artifacts.inputs.append(artifact)
31
+
32
+
23
33
  def maybe_add_testlib(code: CodeItem, artifacts: steps.GradingArtifacts):
24
34
  # Try to get from compilation files, then from package folder, then from tool.
25
35
  artifact = get_local_artifact('testlib.h') or steps.testlib_grading_input()
rbx/box/environment.py CHANGED
@@ -4,7 +4,7 @@ from enum import Enum
4
4
  from typing import Annotated, List, Optional, Type, TypeVar
5
5
 
6
6
  import typer
7
- from pydantic import BaseModel, ConfigDict, ValidationError
7
+ from pydantic import BaseModel, ConfigDict, Field, ValidationError
8
8
 
9
9
  from rbx import config, console, utils
10
10
  from rbx.box.extensions import Extensions, LanguageExtensions
@@ -143,6 +143,13 @@ class EnvironmentLanguage(BaseModel):
143
143
  return self.get_extension(name, cls) or cls()
144
144
 
145
145
 
146
+ class TimingConfig(BaseModel):
147
+ model_config = ConfigDict(extra='forbid')
148
+
149
+ # Formula to use to calculate the time limit for the environment.
150
+ formula: str = 'step_up(max(fastest * 3, slowest * 1.5), 100)'
151
+
152
+
146
153
  class Environment(BaseModel):
147
154
  model_config = ConfigDict(extra='forbid')
148
155
 
@@ -167,6 +174,9 @@ class Environment(BaseModel):
167
174
  # Identifier of the preset that should be used when creating new problems.
168
175
  preset: str = 'default'
169
176
 
177
+ # Timing configuration for the environment.
178
+ timing: TimingConfig = Field(default_factory=TimingConfig)
179
+
170
180
  # Extensions to be added to the environment.
171
181
  extensions: Optional[Extensions] = None
172
182
 
rbx/box/header.py ADDED
@@ -0,0 +1,73 @@
1
+ import functools
2
+ import importlib
3
+ import importlib.resources
4
+ import pathlib
5
+ from typing import Callable, Dict, Type
6
+
7
+ from rbx.box import package
8
+ from rbx.box.schema import Primitive
9
+
10
+
11
+ @functools.cache
12
+ def get_header() -> pathlib.Path:
13
+ generate_header()
14
+
15
+ return pathlib.Path('rbx.h')
16
+
17
+
18
+ def generate_header():
19
+ with importlib.resources.as_file(
20
+ importlib.resources.files('rbx') / 'resources' / 'templates' / 'rbx.h'
21
+ ) as file:
22
+ with file.open('r') as f:
23
+ header = f.read()
24
+
25
+ with pathlib.Path('rbx.h').open('w') as f:
26
+ f.write(_preprocess_header(header))
27
+
28
+
29
+ def _preprocess_header(header: str) -> str:
30
+ return (
31
+ header.replace('//<rbx::string_var>', _get_string_var_block())
32
+ .replace('//<rbx::int_var>', _get_int_var_block())
33
+ .replace('//<rbx::float_var>', _get_float_var_block())
34
+ .replace('//<rbx::bool_var>', _get_bool_var_block())
35
+ )
36
+
37
+
38
+ def _get_string_var_block() -> str:
39
+ return _get_var_block(_get_vars_of_type(str, lambda x: f'{x:!r}'))
40
+
41
+
42
+ def _get_int_var_block() -> str:
43
+ return _get_var_block(_get_vars_of_type(int, lambda x: str(x)))
44
+
45
+
46
+ def _get_float_var_block() -> str:
47
+ return _get_var_block(_get_vars_of_type(float, lambda x: f'{x}'))
48
+
49
+
50
+ def _get_bool_var_block() -> str:
51
+ return _get_var_block(_get_vars_of_type(bool, lambda x: 'true' if x else 'false'))
52
+
53
+
54
+ def _get_vars_of_type(
55
+ type: Type, transform: Callable[[Primitive], str]
56
+ ) -> Dict[str, str]:
57
+ pkg = package.find_problem_package_or_die()
58
+ vars = pkg.expanded_vars
59
+ return {
60
+ name: transform(value)
61
+ for name, value in vars.items()
62
+ if isinstance(value, type)
63
+ }
64
+
65
+
66
+ def _get_var_block(mappings: Dict[str, str]) -> str:
67
+ entries = []
68
+ # Iterate over sorted keys to ensure a deterministic order.
69
+ for name in sorted(mappings):
70
+ value = mappings[name]
71
+ entry = f' if (name == "{name}") {{\n return {value};\n }}\n'
72
+ entries.append(entry)
73
+ return ''.join(entries)
rbx/box/main.py CHANGED
@@ -1,2 +1,6 @@
1
1
  # flake8: noqa
2
+ import nest_asyncio
3
+
4
+ nest_asyncio.apply()
5
+
2
6
  from rbx.box.cli import app
rbx/box/naming.py CHANGED
@@ -20,3 +20,12 @@ def get_problem_shortname() -> Optional[str]:
20
20
  return problem.short_name
21
21
 
22
22
  return None
23
+
24
+
25
+ def get_problem_name_with_contest_info() -> str:
26
+ problem = package.find_problem_package_or_die()
27
+ contest = contest_package.find_contest_package()
28
+ short_name = get_problem_shortname()
29
+ if contest is None or short_name is None:
30
+ return problem.name
31
+ return f'{contest.name}-{short_name}-{problem.name}'
@@ -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 naming, package
9
+ from rbx.box import header, 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
@@ -150,9 +150,11 @@ class BocaPackager(BasePackager):
150
150
  checker_text = checker_path.read_text()
151
151
  testlib = get_testlib().read_text()
152
152
  checker = package.get_checker().path.read_text()
153
+ rbx_header = header.get_header().read_text()
153
154
  return (
154
155
  checker_text.replace('{{rbxFlags}}', extension.flags_with_defaults()['cc'])
155
156
  .replace('{{testlib_content}}', testlib)
157
+ .replace('{{rbx_header_content}}', rbx_header)
156
158
  .replace('{{checker_content}}', checker)
157
159
  )
158
160
 
@@ -2,6 +2,7 @@ import pathlib
2
2
  import tempfile
3
3
  from typing import Type
4
4
 
5
+ import syncer
5
6
  import typer
6
7
 
7
8
  from rbx import annotations, console
@@ -18,7 +19,7 @@ from rbx.box.packaging.packager import (
18
19
  app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
19
20
 
20
21
 
21
- def run_contest_packager(
22
+ async def run_contest_packager(
22
23
  contest_packager_cls: Type[BaseContestPackager],
23
24
  packager_cls: Type[BasePackager],
24
25
  verification: environment.VerificationParam,
@@ -33,7 +34,7 @@ def run_contest_packager(
33
34
  )
34
35
  with cd.new_package_cd(problem.get_path()):
35
36
  package.clear_package_cache()
36
- package_path = run_packager(packager_cls, verification=verification)
37
+ package_path = await run_packager(packager_cls, verification=verification)
37
38
  built_packages.append(
38
39
  BuiltProblemPackage(
39
40
  path=problem.get_path() / package_path,
@@ -73,7 +74,8 @@ def run_contest_packager(
73
74
 
74
75
  @app.command('polygon', help='Build a contest package for Polygon.')
75
76
  @contest_package.within_contest
76
- def polygon(
77
+ @syncer.sync
78
+ async def polygon(
77
79
  verification: environment.VerificationParam,
78
80
  ):
79
81
  from rbx.box.packaging.polygon.packager import (
@@ -81,6 +83,6 @@ def polygon(
81
83
  PolygonPackager,
82
84
  )
83
85
 
84
- run_contest_packager(
86
+ await run_contest_packager(
85
87
  PolygonContestPackager, PolygonPackager, verification=verification
86
88
  )
rbx/box/packaging/main.py CHANGED
@@ -6,7 +6,8 @@ import syncer
6
6
  import typer
7
7
 
8
8
  from rbx import annotations, console
9
- from rbx.box import environment, package
9
+ from rbx.box import environment, header, package
10
+ from rbx.box.naming import get_problem_name_with_contest_info
10
11
  from rbx.box.package import get_build_path
11
12
  from rbx.box.packaging.packager import BasePackager, BuiltStatement
12
13
  from rbx.box.statements.build_statements import build_statement
@@ -21,6 +22,8 @@ async def run_packager(
21
22
  ) -> pathlib.Path:
22
23
  from rbx.box import builder
23
24
 
25
+ header.generate_header()
26
+
24
27
  if not await builder.verify(verification=verification):
25
28
  console.console.print(
26
29
  '[error]Build or verification failed, check the report.[/error]'
@@ -68,11 +71,22 @@ async def run_packager(
68
71
  @syncer.sync
69
72
  async def polygon(
70
73
  verification: environment.VerificationParam,
74
+ upload: bool = typer.Option(
75
+ False,
76
+ '--upload',
77
+ '-u',
78
+ help='If set, will upload the package to Polygon.',
79
+ ),
71
80
  ):
72
81
  from rbx.box.packaging.polygon.packager import PolygonPackager
73
82
 
74
83
  await run_packager(PolygonPackager, verification=verification)
75
84
 
85
+ if upload:
86
+ from rbx.box.packaging.polygon.upload import upload_problem
87
+
88
+ await upload_problem(name=get_problem_name_with_contest_info())
89
+
76
90
 
77
91
  @app.command('boca', help='Build a package for BOCA.')
78
92
  @package.within_problem
@@ -5,7 +5,7 @@ from typing import List
5
5
  import typer
6
6
 
7
7
  from rbx import console
8
- from rbx.box import package
8
+ from rbx.box import header, package
9
9
  from rbx.box.environment import get_extension_or_default
10
10
  from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
11
11
  from rbx.box.packaging.boca.packager import BocaPackager
@@ -153,6 +153,11 @@ class MojPackager(BocaPackager):
153
153
  testlib_path.parent.mkdir(parents=True, exist_ok=True)
154
154
  testlib_path.write_text(get_testlib().read_text())
155
155
 
156
+ # Prepare rbx.h
157
+ rbx_header_path = into_path / 'scripts' / 'rbx.h'
158
+ rbx_header_path.parent.mkdir(parents=True, exist_ok=True)
159
+ rbx_header_path.write_text(header.get_header().read_text())
160
+
156
161
  # Prepare checker
157
162
  checker_path = into_path / 'scripts' / 'checker.cpp'
158
163
  checker_path.parent.mkdir(parents=True, exist_ok=True)
@@ -1,13 +1,13 @@
1
1
  import functools
2
2
  import pathlib
3
3
  import shutil
4
- from typing import List
4
+ from typing import List, Optional
5
5
 
6
6
  import iso639
7
7
  import typer
8
8
 
9
9
  from rbx import console
10
- from rbx.box import package
10
+ from rbx.box import header, package
11
11
  from rbx.box.packaging.packager import (
12
12
  BaseContestPackager,
13
13
  BasePackager,
@@ -16,6 +16,7 @@ from rbx.box.packaging.packager import (
16
16
  BuiltStatement,
17
17
  )
18
18
  from rbx.box.packaging.polygon import xml_schema as polygon_schema
19
+ from rbx.box.schema import TaskType
19
20
  from rbx.config import get_testlib
20
21
 
21
22
  DAT_TEMPLATE = """
@@ -36,7 +37,7 @@ def code_to_langs(langs: List[str]) -> List[str]:
36
37
 
37
38
 
38
39
  @functools.cache
39
- def _is_valid_lang_code(lang: str) -> bool:
40
+ def is_valid_lang_code(lang: str) -> bool:
40
41
  try:
41
42
  code_to_langs([lang])
42
43
  except iso639.LanguageNotFoundError:
@@ -49,6 +50,10 @@ def _is_valid_lang_code(lang: str) -> bool:
49
50
 
50
51
 
51
52
  class PolygonPackager(BasePackager):
53
+ @classmethod
54
+ def task_types(cls) -> List[TaskType]:
55
+ return [TaskType.BATCH, TaskType.COMMUNICATION]
56
+
52
57
  def _validate(self):
53
58
  langs = self.languages()
54
59
  pkg = package.find_problem_package_or_die()
@@ -83,6 +88,14 @@ class PolygonPackager(BasePackager):
83
88
  cpy=polygon_schema.File(path='check.cpp'),
84
89
  )
85
90
 
91
+ def _get_interactor(self) -> Optional[polygon_schema.Interactor]:
92
+ pkg = package.find_problem_package_or_die()
93
+ if pkg.interactor is None:
94
+ return None
95
+ return polygon_schema.Interactor(
96
+ source=polygon_schema.File(path='files/interactor.cpp', type='cpp.g++17'),
97
+ )
98
+
86
99
  def _get_manual_test(self) -> polygon_schema.Test:
87
100
  # TODO: return samples
88
101
  return polygon_schema.Test(method='manual')
@@ -118,7 +131,10 @@ class PolygonPackager(BasePackager):
118
131
  return polygon_schema.Judging(testsets=[self._get_single_testset()])
119
132
 
120
133
  def _get_files(self) -> List[polygon_schema.File]:
121
- return [polygon_schema.File(path='files/testlib.h', type='h.g++')]
134
+ return [
135
+ polygon_schema.File(path='files/testlib.h', type='h.g++'),
136
+ polygon_schema.File(path='files/rbx.h', type='h.g++'),
137
+ ]
122
138
 
123
139
  def _statement_application_type(self, statement: BuiltStatement) -> str:
124
140
  return 'application/pdf'
@@ -148,7 +164,8 @@ class PolygonPackager(BasePackager):
148
164
  for built_statement in built_statements
149
165
  ]
150
166
 
151
- def name(self) -> str:
167
+ @classmethod
168
+ def name(cls) -> str:
152
169
  return 'polygon'
153
170
 
154
171
  def package(
@@ -157,11 +174,14 @@ class PolygonPackager(BasePackager):
157
174
  into_path: pathlib.Path,
158
175
  built_statements: List[BuiltStatement],
159
176
  ):
177
+ pkg = package.find_problem_package_or_die()
178
+
160
179
  problem = polygon_schema.Problem(
161
180
  names=self._get_names(),
162
181
  checker=self._get_checker(),
163
182
  judging=self._get_judging(),
164
183
  files=self._get_files(),
184
+ interactor=self._get_interactor(),
165
185
  # TODO: revisit polygon problem statements
166
186
  # statements=self._process_statements(built_statements, into_path),
167
187
  )
@@ -179,8 +199,11 @@ class PolygonPackager(BasePackager):
179
199
  files_path = into_path / 'files'
180
200
  files_path.mkdir(parents=True, exist_ok=True)
181
201
  shutil.copyfile(get_testlib(), files_path / 'testlib.h')
202
+ shutil.copyfile(header.get_header(), files_path / 'rbx.h')
182
203
  shutil.copyfile(package.get_checker().path, files_path / 'check.cpp')
183
204
  shutil.copyfile(package.get_checker().path, into_path / 'check.cpp')
205
+ if pkg.interactor is not None:
206
+ shutil.copyfile(pkg.interactor.path, files_path / 'interactor.cpp')
184
207
 
185
208
  # Copy all testcases
186
209
  (into_path / 'tests').mkdir(parents=True, exist_ok=True)
@@ -219,7 +242,7 @@ class PolygonContestPackager(BaseContestPackager):
219
242
  value=self.get_statement_for_language(lang).title,
220
243
  )
221
244
  for lang in self.languages()
222
- if _is_valid_lang_code(lang)
245
+ if is_valid_lang_code(lang)
223
246
  ]
224
247
  if names:
225
248
  names[0].main = True
@@ -284,7 +307,7 @@ class PolygonContestPackager(BaseContestPackager):
284
307
  ) -> pathlib.Path:
285
308
  filtered_statements = []
286
309
  for built_statement in built_statements:
287
- if not _is_valid_lang_code(built_statement.statement.language):
310
+ if not is_valid_lang_code(built_statement.statement.language):
288
311
  continue
289
312
  filtered_statements.append(built_statement)
290
313