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/config.py ADDED
@@ -0,0 +1,271 @@
1
+ import functools
2
+ import importlib
3
+ import importlib.resources
4
+ import os
5
+ import pathlib
6
+ import shutil
7
+ import subprocess
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ import requests
11
+ import typer
12
+ from pydantic import BaseModel
13
+
14
+ from rbx import utils
15
+ from rbx.console import console
16
+ from rbx.grading.judge.storage import copyfileobj
17
+
18
+ app = typer.Typer(no_args_is_help=True)
19
+
20
+ _RESOURCES_PKG = 'rbx.resources'
21
+ _CONFIG_FILE_NAME = 'default_config.json'
22
+
23
+
24
+ def format_vars(template: str, **kwargs) -> str:
25
+ res = template
26
+ for key, value in kwargs.items():
27
+ key = key.replace('_', '-')
28
+ res = res.replace(f'%{{{key}}}', value)
29
+ return res
30
+
31
+
32
+ class Artifact(BaseModel):
33
+ filename: Optional[str] = None
34
+ executable: bool = False
35
+ optional: bool = False
36
+
37
+
38
+ class Language(BaseModel):
39
+ template: str
40
+ file: str
41
+ submitFile: Optional[str] = None
42
+ preprocess: Optional[List[str]] = None
43
+ exec: str
44
+ artifacts: Dict[str, Optional[Artifact]] = {}
45
+ submitor: Optional[str] = None
46
+
47
+ def get_file(self, basename: str) -> str:
48
+ return format_vars(self.file, problem_code=basename)
49
+
50
+ def has_submit_file(self) -> bool:
51
+ return self.submitFile is not None
52
+
53
+ def get_submit_file(self, basename: str) -> str:
54
+ if not self.submitFile:
55
+ return self.get_file(basename)
56
+ return format_vars(
57
+ self.submitFile, file=self.get_file(basename), problem_code=basename
58
+ )
59
+
60
+ def get_template(self) -> str:
61
+ return get_app_file(pathlib.Path('templates') / self.template).read_text()
62
+
63
+
64
+ SubmitorConfig = Dict[str, Any]
65
+ Credentials = Dict[str, Any]
66
+
67
+
68
+ class Config(BaseModel):
69
+ defaultLanguage: str
70
+ languages: Dict[str, Language]
71
+ editor: Optional[str] = None
72
+ submitor: Dict[str, SubmitorConfig]
73
+ credentials: Credentials
74
+ boxEnvironment: str = 'default'
75
+
76
+ def get_default_language(self) -> Optional[Language]:
77
+ return self.languages.get(self.defaultLanguage)
78
+
79
+ def get_language(self, name: Optional[str] = None) -> Optional[Language]:
80
+ return self.languages.get(name or self.defaultLanguage)
81
+
82
+
83
+ def get_app_path() -> pathlib.Path:
84
+ return utils.get_app_path()
85
+
86
+
87
+ def get_empty_app_persist_path() -> pathlib.Path:
88
+ app_dir = get_app_path() / 'persist'
89
+ shutil.rmtree(str(app_dir), ignore_errors=True)
90
+ app_dir.mkdir(parents=True, exist_ok=True)
91
+ return app_dir
92
+
93
+
94
+ def get_app_file(path: pathlib.Path) -> pathlib.Path:
95
+ file_path = get_app_path() / path
96
+ if file_path.is_file():
97
+ return file_path
98
+
99
+ with importlib.resources.as_file(
100
+ importlib.resources.files('rbx') / 'resources' / path # type: ignore
101
+ ) as file:
102
+ if file.is_file():
103
+ file_path.parent.mkdir(parents=True, exist_ok=True)
104
+ with file.open('rb') as fr:
105
+ with file_path.open('wb') as fw:
106
+ copyfileobj(fr, fw)
107
+ return file_path
108
+
109
+
110
+ def _download_checker(name: str, save_at: pathlib.Path):
111
+ console.print(f'Downloading checker {name}...')
112
+ r = requests.get(
113
+ f'https://raw.githubusercontent.com/MikeMirzayanov/testlib/master/checkers/{name}'
114
+ )
115
+
116
+ if r.ok:
117
+ save_at.parent.mkdir(parents=True, exist_ok=True)
118
+ with save_at.open('wb') as f:
119
+ f.write(r.content)
120
+
121
+
122
+ def _download_testlib(save_at: pathlib.Path):
123
+ console.print('Downloading testlib.h...')
124
+ r = requests.get(
125
+ 'https://raw.githubusercontent.com/MikeMirzayanov/testlib/master/testlib.h'
126
+ )
127
+
128
+ if r.ok:
129
+ save_at.parent.mkdir(parents=True, exist_ok=True)
130
+ with save_at.open('wb') as f:
131
+ f.write(r.content)
132
+ else:
133
+ console.print('[error]Failed to download testlib.h.[/error]')
134
+ raise typer.Exit(1)
135
+
136
+
137
+ def _download_jngen(save_at: pathlib.Path):
138
+ console.print('Downloading jngen.h...')
139
+ r = requests.get('https://raw.githubusercontent.com/ifsmirnov/jngen/master/jngen.h')
140
+
141
+ if r.ok:
142
+ save_at.parent.mkdir(parents=True, exist_ok=True)
143
+ with save_at.open('wb') as f:
144
+ f.write(r.content)
145
+ else:
146
+ console.print('[error]Failed to download jngen.h.[/error]')
147
+ raise typer.Exit(1)
148
+
149
+
150
+ def _download_bits_stdcpp(save_at: pathlib.Path):
151
+ console.print('Downloading bits/stdc++.h...')
152
+ r = requests.get(
153
+ 'https://raw.githubusercontent.com/tekfyl/bits-stdc-.h-for-mac/master/stdc%2B%2B.h'
154
+ )
155
+
156
+ if r.ok:
157
+ save_at.parent.mkdir(parents=True, exist_ok=True)
158
+ with save_at.open('wb') as f:
159
+ f.write(r.content)
160
+ else:
161
+ console.print('[error]Failed to download bits/stdc++.h.[/error]')
162
+ raise typer.Exit(1)
163
+
164
+
165
+ def get_builtin_checker(name: str) -> pathlib.Path:
166
+ app_file = get_app_file(pathlib.Path('checkers') / name)
167
+ if not app_file.exists():
168
+ _download_checker(name, app_file)
169
+ return app_file
170
+
171
+
172
+ def get_testlib() -> pathlib.Path:
173
+ app_file = get_app_file(pathlib.Path('testlib.h'))
174
+ if not app_file.exists():
175
+ _download_testlib(app_file)
176
+ return app_file
177
+
178
+
179
+ def get_jngen() -> pathlib.Path:
180
+ app_file = get_app_file(pathlib.Path('jngen.h'))
181
+ if not app_file.exists():
182
+ _download_jngen(app_file)
183
+ return app_file
184
+
185
+
186
+ def get_bits_stdcpp() -> pathlib.Path:
187
+ app_file = get_app_file(pathlib.Path('stdc++.h'))
188
+ if not app_file.exists():
189
+ _download_bits_stdcpp(app_file)
190
+ return app_file
191
+
192
+
193
+ def get_default_config_path() -> pathlib.Path:
194
+ with importlib.resources.as_file(
195
+ importlib.resources.files('rbx') / 'resources' / _CONFIG_FILE_NAME
196
+ ) as file:
197
+ return file
198
+
199
+
200
+ def get_default_app_path() -> pathlib.Path:
201
+ return get_default_config_path().parent
202
+
203
+
204
+ def get_default_config() -> Config:
205
+ return Config.model_validate_json(get_default_config_path().read_text())
206
+
207
+
208
+ def get_config_path() -> pathlib.Path:
209
+ return get_app_path() / 'config.json'
210
+
211
+
212
+ def get_editor():
213
+ return get_config().editor or os.environ.get('EDITOR', None)
214
+
215
+
216
+ def open_editor(path: pathlib.Path, *args):
217
+ editor = get_editor()
218
+ if editor is None:
219
+ raise Exception('No editor found. Please set the EDITOR environment variable.')
220
+ subprocess.run([editor, str(path), *[str(arg) for arg in args]])
221
+
222
+
223
+ @functools.cache
224
+ def get_config() -> Config:
225
+ config_path = get_config_path()
226
+ if not config_path.is_file():
227
+ utils.create_and_write(config_path, utils.model_json(get_default_config()))
228
+ return Config.model_validate_json(config_path.read_text())
229
+
230
+
231
+ def save_config(cfg: Config):
232
+ cfg_path = get_config_path()
233
+ cfg_path.write_text(utils.model_json(cfg))
234
+ get_config.cache_clear()
235
+
236
+
237
+ @app.command()
238
+ def path():
239
+ """
240
+ Show the absolute path of the config file.
241
+ """
242
+ get_config() # Ensure config is created.
243
+ console.print(get_config_path())
244
+
245
+
246
+ @app.command('list, ls')
247
+ def list():
248
+ """
249
+ Pretty print the config file.
250
+ """
251
+ console.print_json(utils.model_json(get_config()))
252
+
253
+
254
+ @app.command()
255
+ def reset():
256
+ """
257
+ Reset the config file to the default one.
258
+ """
259
+ if not typer.confirm('Do you really want to reset your config to the default one?'):
260
+ return
261
+ cfg_path = get_config_path()
262
+ cfg_path.unlink(missing_ok=True)
263
+ get_config() # Reset the config.
264
+
265
+
266
+ @app.command('edit, e')
267
+ def edit():
268
+ """
269
+ Open the config in an editor.
270
+ """
271
+ open_editor(get_config_path())
rbx/conftest.py ADDED
@@ -0,0 +1,38 @@
1
+ import os
2
+ import pathlib
3
+ import shutil
4
+ import tempfile
5
+ from collections.abc import Iterator
6
+
7
+ import pytest
8
+
9
+ from rbx.testing_utils import get_testdata_path
10
+
11
+
12
+ @pytest.fixture
13
+ def testdata_path() -> pathlib.Path:
14
+ return get_testdata_path()
15
+
16
+
17
+ @pytest.fixture
18
+ def cleandir() -> Iterator[pathlib.Path]:
19
+ with tempfile.TemporaryDirectory() as newpath:
20
+ abspath = pathlib.Path(newpath).absolute()
21
+ old_cwd = pathlib.Path.cwd()
22
+ os.chdir(newpath)
23
+ try:
24
+ yield abspath
25
+ finally:
26
+ os.chdir(str(old_cwd))
27
+
28
+
29
+ @pytest.fixture
30
+ def cleandir_with_testdata(
31
+ request, testdata_path: pathlib.Path, cleandir: pathlib.Path
32
+ ) -> Iterator[pathlib.Path]:
33
+ marker = request.node.get_closest_marker('test_pkg')
34
+ if marker is None:
35
+ raise ValueError('test_pkg marker not found')
36
+ testdata = testdata_path / marker.args[0]
37
+ shutil.copytree(str(testdata), str(cleandir), dirs_exist_ok=True)
38
+ yield cleandir
rbx/console.py ADDED
@@ -0,0 +1,27 @@
1
+ import sys
2
+
3
+ from rich.console import Console
4
+ from rich.theme import Theme
5
+
6
+ theme = Theme(
7
+ {
8
+ 'default': 'bright_white',
9
+ 'rbx': 'bold italic yellow',
10
+ 'info': 'bright_black',
11
+ 'status': 'bright_white',
12
+ 'item': 'bold blue',
13
+ 'error': 'bold red',
14
+ 'success': 'bold green',
15
+ 'lnumber': 'dim cyan',
16
+ 'warning': 'bold yellow',
17
+ }
18
+ )
19
+ console = Console(theme=theme, style='info', highlight=False)
20
+ stderr_console = Console(theme=theme, style='info', highlight=False, stderr=True)
21
+
22
+
23
+ def multiline_prompt(text: str) -> str:
24
+ console.print(f'{text} (Ctrl-D to finish):\n')
25
+ lines = sys.stdin.readlines()
26
+ console.print()
27
+ return ''.join(lines)
rbx/create.py ADDED
@@ -0,0 +1,37 @@
1
+ import pathlib
2
+
3
+ from rbx import annotations
4
+ from rbx.clone import create_problem_structure
5
+ from rbx.config import get_config
6
+ from rbx.console import console
7
+ from rbx.schema import Batch, Problem
8
+
9
+
10
+ def main(
11
+ name: str,
12
+ lang: annotations.Language,
13
+ timelimit: annotations.Timelimit = 1000,
14
+ memorylimit: annotations.Memorylimit = 256,
15
+ multitest: annotations.Multitest = False,
16
+ ):
17
+ language = get_config().get_language(lang)
18
+ if language is None:
19
+ console.print(
20
+ f'[error]Language {lang or get_config().defaultLanguage} not found in config. Please check your configuration.[/error]'
21
+ )
22
+ return
23
+
24
+ problem = Problem(
25
+ name=name,
26
+ timeLimit=timelimit,
27
+ memoryLimit=memorylimit,
28
+ testType='multiNumber' if multitest else 'single',
29
+ batch=Batch.create(),
30
+ )
31
+ create_problem_structure(
32
+ pathlib.Path(),
33
+ problem,
34
+ language,
35
+ status=None,
36
+ verbose=True,
37
+ )
rbx/edit.py ADDED
@@ -0,0 +1,24 @@
1
+ import pathlib
2
+ from typing import Optional
3
+
4
+ from rbx import annotations, console, metadata
5
+ from rbx.config import get_config, open_editor
6
+
7
+
8
+ def main(problem: str, language: Optional[annotations.LanguageWithDefault] = None):
9
+ lang = get_config().get_language(language)
10
+ if lang is None:
11
+ console.console.print(
12
+ f'[error]Language {language or get_config().defaultLanguage} not found in config. Please check your configuration.[/error]'
13
+ )
14
+ return
15
+
16
+ dumped_problem = metadata.find_problem_by_anything(problem)
17
+ if not dumped_problem:
18
+ console.console.print(
19
+ f'[error]Problem with identifier {problem} not found.[/error]'
20
+ )
21
+ return
22
+
23
+ filename = lang.get_file(dumped_problem.code)
24
+ open_editor(pathlib.Path(filename))
File without changes