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.
Files changed (164) hide show
  1. rbx/__init__.py +0 -0
  2. rbx/annotations.py +127 -0
  3. rbx/autoenum.py +333 -0
  4. rbx/box/__init__.py +0 -0
  5. rbx/box/builder.py +77 -0
  6. rbx/box/cd.py +37 -0
  7. rbx/box/checkers.py +134 -0
  8. rbx/box/code.py +185 -0
  9. rbx/box/compile.py +56 -0
  10. rbx/box/conftest.py +42 -0
  11. rbx/box/contest/__init__.py +0 -0
  12. rbx/box/contest/build_contest_statements.py +347 -0
  13. rbx/box/contest/contest_package.py +76 -0
  14. rbx/box/contest/contest_utils.py +20 -0
  15. rbx/box/contest/main.py +179 -0
  16. rbx/box/contest/schema.py +155 -0
  17. rbx/box/contest/statements.py +82 -0
  18. rbx/box/creation.py +72 -0
  19. rbx/box/download.py +64 -0
  20. rbx/box/environment.py +345 -0
  21. rbx/box/extensions.py +26 -0
  22. rbx/box/generators.py +478 -0
  23. rbx/box/generators_test.py +63 -0
  24. rbx/box/main.py +449 -0
  25. rbx/box/package.py +316 -0
  26. rbx/box/packaging/boca/extension.py +27 -0
  27. rbx/box/packaging/boca/packager.py +245 -0
  28. rbx/box/packaging/contest_main.py +82 -0
  29. rbx/box/packaging/main.py +68 -0
  30. rbx/box/packaging/packager.py +117 -0
  31. rbx/box/packaging/polygon/packager.py +320 -0
  32. rbx/box/packaging/polygon/test.py +81 -0
  33. rbx/box/packaging/polygon/xml_schema.py +106 -0
  34. rbx/box/presets/__init__.py +503 -0
  35. rbx/box/presets/fetch.py +70 -0
  36. rbx/box/presets/lock_schema.py +20 -0
  37. rbx/box/presets/schema.py +59 -0
  38. rbx/box/schema.py +394 -0
  39. rbx/box/solutions.py +792 -0
  40. rbx/box/solutions_test.py +41 -0
  41. rbx/box/statements/__init__.py +0 -0
  42. rbx/box/statements/build_statements.py +359 -0
  43. rbx/box/statements/builders.py +375 -0
  44. rbx/box/statements/joiners.py +113 -0
  45. rbx/box/statements/latex.py +47 -0
  46. rbx/box/statements/latex_jinja.py +214 -0
  47. rbx/box/statements/schema.py +138 -0
  48. rbx/box/stresses.py +292 -0
  49. rbx/box/stressing/__init__.py +0 -0
  50. rbx/box/stressing/finder_parser.py +359 -0
  51. rbx/box/stressing/generator_parser.py +258 -0
  52. rbx/box/testcases.py +54 -0
  53. rbx/box/ui/__init__.py +0 -0
  54. rbx/box/ui/captured_log.py +372 -0
  55. rbx/box/ui/css/app.tcss +48 -0
  56. rbx/box/ui/main.py +38 -0
  57. rbx/box/ui/run.py +209 -0
  58. rbx/box/validators.py +245 -0
  59. rbx/box/validators_test.py +15 -0
  60. rbx/checker.py +128 -0
  61. rbx/clone.py +197 -0
  62. rbx/config.py +271 -0
  63. rbx/conftest.py +38 -0
  64. rbx/console.py +27 -0
  65. rbx/create.py +37 -0
  66. rbx/edit.py +24 -0
  67. rbx/grading/__init__.py +0 -0
  68. rbx/grading/caching.py +356 -0
  69. rbx/grading/conftest.py +33 -0
  70. rbx/grading/judge/__init__.py +0 -0
  71. rbx/grading/judge/cacher.py +503 -0
  72. rbx/grading/judge/digester.py +35 -0
  73. rbx/grading/judge/sandbox.py +748 -0
  74. rbx/grading/judge/sandboxes/__init__.py +0 -0
  75. rbx/grading/judge/sandboxes/isolate.py +683 -0
  76. rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
  77. rbx/grading/judge/sandboxes/timeit.py +217 -0
  78. rbx/grading/judge/storage.py +284 -0
  79. rbx/grading/judge/test.py +38 -0
  80. rbx/grading/judge/testiso.py +54 -0
  81. rbx/grading/steps.py +522 -0
  82. rbx/grading/steps_with_caching.py +59 -0
  83. rbx/grading/steps_with_caching_run_test.py +429 -0
  84. rbx/grading_utils.py +148 -0
  85. rbx/hydration.py +101 -0
  86. rbx/main.py +122 -0
  87. rbx/metadata.py +105 -0
  88. rbx/providers/__init__.py +43 -0
  89. rbx/providers/codeforces.py +73 -0
  90. rbx/providers/provider.py +26 -0
  91. rbx/resources/checkers/boilerplate.cpp +20 -0
  92. rbx/resources/default_config.json +48 -0
  93. rbx/resources/envs/default.rbx.yml +37 -0
  94. rbx/resources/envs/isolate.rbx.yml +37 -0
  95. rbx/resources/packagers/boca/checker.sh +43 -0
  96. rbx/resources/packagers/boca/compare +53 -0
  97. rbx/resources/packagers/boca/compile/c +172 -0
  98. rbx/resources/packagers/boca/compile/cc +173 -0
  99. rbx/resources/packagers/boca/compile/cpp +172 -0
  100. rbx/resources/packagers/boca/compile/java +194 -0
  101. rbx/resources/packagers/boca/compile/kt +155 -0
  102. rbx/resources/packagers/boca/compile/pas +172 -0
  103. rbx/resources/packagers/boca/compile/py2 +173 -0
  104. rbx/resources/packagers/boca/compile/py3 +173 -0
  105. rbx/resources/packagers/boca/run/c +128 -0
  106. rbx/resources/packagers/boca/run/cc +128 -0
  107. rbx/resources/packagers/boca/run/cpp +128 -0
  108. rbx/resources/packagers/boca/run/java +194 -0
  109. rbx/resources/packagers/boca/run/kt +159 -0
  110. rbx/resources/packagers/boca/run/py2 +166 -0
  111. rbx/resources/packagers/boca/run/py3 +166 -0
  112. rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
  113. rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
  114. rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
  115. rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
  116. rbx/resources/presets/default/preset.rbx.yml +12 -0
  117. rbx/resources/presets/default/problem/.gitignore +6 -0
  118. rbx/resources/presets/default/problem/gen.cpp +9 -0
  119. rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
  120. rbx/resources/presets/default/problem/random.py +3 -0
  121. rbx/resources/presets/default/problem/random.txt +2 -0
  122. rbx/resources/presets/default/problem/sols/main.cpp +9 -0
  123. rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
  124. rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
  125. rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
  126. rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  127. rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
  128. rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
  129. rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
  130. rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
  131. rbx/resources/presets/default/problem/validator.cpp +16 -0
  132. rbx/resources/presets/default/problem/wcmp.cpp +34 -0
  133. rbx/resources/templates/template.cpp +19 -0
  134. rbx/run.py +45 -0
  135. rbx/schema.py +64 -0
  136. rbx/submit.py +61 -0
  137. rbx/submitors/__init__.py +18 -0
  138. rbx/submitors/codeforces.py +120 -0
  139. rbx/submitors/submitor.py +25 -0
  140. rbx/test.py +347 -0
  141. rbx/testcase.py +70 -0
  142. rbx/testcase_rendering.py +79 -0
  143. rbx/testdata/box1/gen1.cpp +7 -0
  144. rbx/testdata/box1/gen2.cpp +9 -0
  145. rbx/testdata/box1/genScript.py +2 -0
  146. rbx/testdata/box1/hard-tle.sol.cpp +26 -0
  147. rbx/testdata/box1/ole.cpp +17 -0
  148. rbx/testdata/box1/problem.rbx.yml +39 -0
  149. rbx/testdata/box1/re.sol.cpp +23 -0
  150. rbx/testdata/box1/sol.cpp +22 -0
  151. rbx/testdata/box1/tests/1.in +1 -0
  152. rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
  153. rbx/testdata/box1/tle.sol.cpp +35 -0
  154. rbx/testdata/box1/validator.cpp +11 -0
  155. rbx/testdata/box1/wa.sol.cpp +22 -0
  156. rbx/testdata/caching/executable.py +1 -0
  157. rbx/testdata/compatible +0 -0
  158. rbx/testing_utils.py +65 -0
  159. rbx/utils.py +162 -0
  160. rbx_cp-0.5.0.dist-info/LICENSE +201 -0
  161. rbx_cp-0.5.0.dist-info/METADATA +89 -0
  162. rbx_cp-0.5.0.dist-info/RECORD +164 -0
  163. rbx_cp-0.5.0.dist-info/WHEEL +4 -0
  164. rbx_cp-0.5.0.dist-info/entry_points.txt +4 -0
rbx/box/environment.py ADDED
@@ -0,0 +1,345 @@
1
+ import functools
2
+ import pathlib
3
+ from enum import Enum
4
+ from typing import Annotated, List, Optional, Type, TypeVar
5
+
6
+ import typer
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+ from rbx import config, console, utils
10
+ from rbx.box.extensions import Extensions, LanguageExtensions
11
+ from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
12
+ from rbx.grading.judge.sandboxes.isolate import IsolateSandbox
13
+ from rbx.grading.judge.sandboxes.stupid_sandbox import StupidSandbox
14
+
15
+ T = TypeVar('T', bound=BaseModel)
16
+
17
+
18
+ class VerificationLevel(Enum):
19
+ NONE = 0
20
+ VALIDATE = 1
21
+ FAST_SOLUTIONS = 2
22
+ ASAN = 3
23
+ ALL_SOLUTIONS = 4
24
+ FULL = 5
25
+
26
+
27
+ VerificationParam = Annotated[
28
+ int,
29
+ typer.Option(
30
+ '--verification-level',
31
+ '--verification',
32
+ '-v',
33
+ help='Verification level to use when building package.',
34
+ default_factory=lambda: VerificationLevel.ALL_SOLUTIONS.value,
35
+ ),
36
+ ]
37
+
38
+
39
+ class FileMapping(BaseModel):
40
+ model_config = ConfigDict(extra='forbid')
41
+
42
+ # Path where to copy the stdin file to before running the program,
43
+ # relative to the sandbox root.
44
+ input: str = 'stdin'
45
+
46
+ # Path where to output the stdout file after running the program,
47
+ # relative to the sandbox root.
48
+ output: str = 'stdout'
49
+
50
+ # Path where to output the stderr file after running the program,
51
+ # relative to the sandbox root.
52
+ error: str = 'stderr'
53
+
54
+ # Path where to copy the compilable file to before compiling the program,
55
+ # relative to the sandbox root.
56
+ compilable: str = 'compilable'
57
+
58
+ # Path to where to output the executable file after compiling the program,
59
+ # relative to the sandbox root.
60
+ executable: str = 'executable'
61
+
62
+
63
+ class EnvironmentSandbox(BaseModel):
64
+ model_config = ConfigDict(extra='forbid')
65
+
66
+ # Max. number of process to allow to run concurrently for the program.
67
+ maxProcesses: Optional[int] = 1
68
+
69
+ # Time limit in milliseconds to allow the program to run.
70
+ timeLimit: Optional[int] = None
71
+
72
+ # Wall time limit in milliseconds to allow the program to run.
73
+ wallTimeLimit: Optional[int] = None
74
+
75
+ # Memory limit in MiB.
76
+ memoryLimit: Optional[int] = None
77
+
78
+ # File size limit in KiB
79
+ fileSizeLimit: Optional[int] = None
80
+
81
+ # Stack limit in MiB.
82
+ stackLimit: Optional[int] = None
83
+
84
+ # Whether to preserve env. variables coming from the host.
85
+ preserveEnv: Optional[bool] = False
86
+
87
+ # Directories in the host that should be read-only exposed to the sandbox.
88
+ mirrorDirs: Optional[List[str]] = []
89
+
90
+
91
+ class CompilationConfig(BaseModel):
92
+ # Commands to compile the program.
93
+ commands: Optional[List[str]] = []
94
+
95
+ # Sandbox configuration to use when compiling for this language.
96
+ sandbox: Optional[EnvironmentSandbox] = None
97
+
98
+
99
+ class ExecutionConfig(BaseModel):
100
+ model_config = ConfigDict(extra='forbid')
101
+
102
+ # Command to run the program.
103
+ command: Optional[str] = None
104
+
105
+ # Sandbox configuration to use when executing for this language.
106
+ sandbox: Optional[EnvironmentSandbox] = None
107
+
108
+
109
+ class EnvironmentLanguage(BaseModel):
110
+ model_config = ConfigDict(extra='forbid')
111
+
112
+ # Identifier of this language within this environment.
113
+ name: str
114
+
115
+ # Readable name for this language.
116
+ readable_name: Optional[str] = None
117
+
118
+ # File extension supported by this language. If there's only one language
119
+ # that supports a certain file extension in the environment, the tool
120
+ # will automatically identify the language based on such extension.
121
+ extension: str
122
+
123
+ # Compilation config to use when compiling programs for this language.
124
+ compilation: Optional[CompilationConfig] = None
125
+
126
+ # Execution config to use when running programs for this language.
127
+ execution: ExecutionConfig
128
+
129
+ # Mapping for files within the sandbox. If not specified, the default mapping
130
+ # for the environment will be used.
131
+ fileMapping: Optional[FileMapping] = None
132
+
133
+ # Exntensions to apply for this language.
134
+ extensions: Optional[LanguageExtensions] = None
135
+
136
+ def get_extension(self, name: str, _: Type[T]) -> Optional[T]:
137
+ if self.extensions is None:
138
+ return None
139
+ if hasattr(self.extensions, name):
140
+ return None
141
+ return getattr(self.extensions, name)
142
+
143
+ def get_extension_or_default(self, name: str, cls: Type[T]) -> T:
144
+ return self.get_extension(name, cls) or cls()
145
+
146
+
147
+ class Environment(BaseModel):
148
+ model_config = ConfigDict(extra='forbid')
149
+
150
+ # Default mapping for files within the sandbox. Fields in the mapping can be
151
+ # individually overridden in the language configuration.
152
+ defaultFileMapping: Optional[FileMapping] = None
153
+
154
+ # Default compilation configuration to use when compiling programs. Fields in
155
+ # the compilation config can be individually overridden in the language configuration.
156
+ defaultCompilation: Optional[CompilationConfig] = None
157
+
158
+ # Default execution configuration to use when running programs. Fields in the
159
+ # execution config can be individually overridden in the language configuration.
160
+ defaultExecution: Optional[ExecutionConfig] = None
161
+
162
+ # Configuration for each language supported in this environment.
163
+ languages: List[EnvironmentLanguage] = []
164
+
165
+ # Identifier of the sandbox used by this environment (e.g. "stupid", "isolate")
166
+ sandbox: str = 'stupid'
167
+
168
+ # Identifier of the preset that should be used when creating new problems.
169
+ preset: str = 'default'
170
+
171
+ # Extensions to be added to the environment.
172
+ extensions: Optional[Extensions] = None
173
+
174
+
175
+ def get_environment_path(env: str) -> pathlib.Path:
176
+ return config.get_app_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
177
+
178
+
179
+ def get_local_environment_path() -> Optional[pathlib.Path]:
180
+ # TODO: implement logic to get env from local preset
181
+ return None
182
+
183
+
184
+ @functools.cache
185
+ def get_environment(env: Optional[str] = None) -> Environment:
186
+ env_path = (
187
+ get_environment_path(env) if env is not None else get_local_environment_path()
188
+ )
189
+ if env_path is None:
190
+ env_path = get_environment_path(config.get_config().boxEnvironment)
191
+ if not env_path.is_file():
192
+ console.console.print(
193
+ f'Environment file [item]{env_path}[/item] not found.', style='error'
194
+ )
195
+ raise typer.Exit()
196
+ return utils.model_from_yaml(Environment, env_path.read_text())
197
+
198
+
199
+ @functools.cache
200
+ def get_language(name: str) -> EnvironmentLanguage:
201
+ for lang in get_environment().languages:
202
+ if lang.name == name:
203
+ return lang
204
+ console.console.print(f'Language [item]{name}[/item] not found.', style='error')
205
+ raise typer.Exit()
206
+
207
+
208
+ def install_environment(name: str, file: pathlib.Path):
209
+ if not file.is_file():
210
+ console.console.print(
211
+ f'[error]Environment file [item]{file}[/item] could not be found.'
212
+ )
213
+ raise typer.Exit(1)
214
+
215
+ get_environment_path(name).write_bytes(file.read_bytes())
216
+ console.console.print(
217
+ f'[success]Environment [item]{name}[/item] was installed from [item]{file}[/item].'
218
+ )
219
+
220
+
221
+ def _merge_shallow_models(model: Type[T], base: T, override: T) -> T:
222
+ return model(
223
+ **{
224
+ **base.model_dump(exclude_unset=True),
225
+ **override.model_dump(exclude_unset=True),
226
+ }
227
+ )
228
+
229
+
230
+ def merge_compilation_configs(
231
+ compilation_configs: List[Optional[CompilationConfig]],
232
+ ) -> CompilationConfig:
233
+ merged_cfg = CompilationConfig()
234
+ merged_cfg.sandbox = EnvironmentSandbox(
235
+ maxProcesses=None,
236
+ timeLimit=10000,
237
+ wallTimeLimit=10000,
238
+ memoryLimit=512,
239
+ preserveEnv=True,
240
+ mirrorDirs=['/etc', '/usr'],
241
+ )
242
+ for cfg in compilation_configs:
243
+ if cfg is None:
244
+ continue
245
+ merged_cfg.commands = cfg.commands or merged_cfg.commands
246
+ if cfg.sandbox is not None:
247
+ merged_cfg.sandbox = _merge_shallow_models(
248
+ EnvironmentSandbox, merged_cfg.sandbox, cfg.sandbox
249
+ )
250
+ return merged_cfg
251
+
252
+
253
+ @functools.cache
254
+ def get_compilation_config(language: str) -> CompilationConfig:
255
+ environment = get_environment()
256
+ return merge_compilation_configs(
257
+ [environment.defaultCompilation, get_language(language).compilation]
258
+ )
259
+
260
+
261
+ def merge_execution_configs(
262
+ execution_configs: List[Optional[ExecutionConfig]],
263
+ ) -> ExecutionConfig:
264
+ merged_cfg = ExecutionConfig()
265
+ merged_cfg.sandbox = EnvironmentSandbox()
266
+ for cfg in execution_configs:
267
+ if cfg is None:
268
+ continue
269
+ merged_cfg.command = cfg.command or merged_cfg.command
270
+ if cfg.sandbox is not None:
271
+ merged_cfg.sandbox = _merge_shallow_models(
272
+ EnvironmentSandbox, merged_cfg.sandbox, cfg.sandbox
273
+ )
274
+ return merged_cfg
275
+
276
+
277
+ @functools.cache
278
+ def get_execution_config(language: str) -> ExecutionConfig:
279
+ environment = get_environment()
280
+ return merge_execution_configs(
281
+ [environment.defaultExecution, get_language(language).execution]
282
+ )
283
+
284
+
285
+ @functools.cache
286
+ def get_file_mapping(language: str) -> FileMapping:
287
+ environment = get_environment()
288
+ return _merge_shallow_models(
289
+ FileMapping,
290
+ environment.defaultFileMapping or FileMapping(),
291
+ get_language(language).fileMapping or FileMapping(),
292
+ )
293
+
294
+
295
+ @functools.cache
296
+ def get_sandbox_type() -> Type[SandboxBase]:
297
+ used_sandbox = get_environment().sandbox
298
+ if used_sandbox == 'stupid':
299
+ return StupidSandbox
300
+ if used_sandbox == 'isolate':
301
+ return IsolateSandbox
302
+ return StupidSandbox
303
+
304
+
305
+ def get_mapped_commands(
306
+ commands: List[str], mapping: Optional[FileMapping] = None
307
+ ) -> List[str]:
308
+ mapping = mapping or FileMapping()
309
+ return [cmd.format(**mapping.model_dump()) for cmd in commands]
310
+
311
+
312
+ def get_mapped_command(command: str, mapping: Optional[FileMapping] = None) -> str:
313
+ return get_mapped_commands([command], mapping)[0]
314
+
315
+
316
+ def get_sandbox_params_from_config(
317
+ config: Optional[EnvironmentSandbox],
318
+ ) -> SandboxParams:
319
+ config = config or EnvironmentSandbox()
320
+ params = SandboxParams()
321
+ params.timeout = config.timeLimit
322
+ params.wallclock_timeout = config.wallTimeLimit
323
+ params.address_space = config.memoryLimit
324
+ params.max_processes = config.maxProcesses
325
+ params.fsize = config.fileSizeLimit
326
+ if config.preserveEnv:
327
+ params.preserve_env = True
328
+ if config.mirrorDirs:
329
+ for dir in config.mirrorDirs:
330
+ path = pathlib.Path(dir)
331
+ params.add_mapped_directory(path)
332
+ return params
333
+
334
+
335
+ def get_extension(name: str, _: Type[T]) -> Optional[T]:
336
+ pkg = get_environment()
337
+ if pkg.extensions is None:
338
+ return None
339
+ if hasattr(pkg.extensions, name):
340
+ return None
341
+ return getattr(pkg.extensions, name)
342
+
343
+
344
+ def get_extension_or_default(name: str, cls: Type[T]) -> T:
345
+ return get_extension(name, cls) or cls()
rbx/box/extensions.py ADDED
@@ -0,0 +1,26 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguageExtension
6
+
7
+
8
+ # List of extensions defined in-place.
9
+ class MacExtension(BaseModel):
10
+ gpp_alternative: Optional[str] = None
11
+
12
+
13
+ # Extension abstractions.
14
+ class Extensions(BaseModel):
15
+ mac: Optional[MacExtension] = Field(
16
+ None, description='Extension for setting mac-only configuration.'
17
+ )
18
+ boca: Optional[BocaExtension] = Field(
19
+ None, description='Environment-level extensions for BOCA packaging.'
20
+ )
21
+
22
+
23
+ class LanguageExtensions(BaseModel):
24
+ boca: Optional[BocaLanguageExtension] = Field(
25
+ None, description='Language-level extensions for BOCA packaging.'
26
+ )