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
@@ -0,0 +1,284 @@
1
+ import dataclasses
2
+ import io
3
+ import logging
4
+ import pathlib
5
+ import tempfile
6
+ from abc import ABC, abstractmethod
7
+ from typing import IO, AnyStr, List, Optional
8
+
9
+ import gevent
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ TOMBSTONE = 'x'
14
+
15
+
16
+ def copyfileobj(
17
+ source_fobj: IO[AnyStr],
18
+ destination_fobj: IO[AnyStr],
19
+ buffer_size=io.DEFAULT_BUFFER_SIZE,
20
+ maxlen: Optional[int] = None,
21
+ ):
22
+ """Read all content from one file object and write it to another.
23
+ Repeatedly read from the given source file object, until no content
24
+ is left, and at the same time write the content to the destination
25
+ file object. Never read or write more than the given buffer size.
26
+ Be cooperative with other greenlets by yielding often.
27
+ source_fobj (fileobj): a file object open for reading, in either
28
+ binary or str mode (doesn't need to be buffered).
29
+ destination_fobj (fileobj): a file object open for writing, in the
30
+ same mode as the source (doesn't need to be buffered).
31
+ buffer_size (int): the size of the read/write buffer.
32
+ maxlen (int): the maximum number of bytes to copy. If None, copy all.
33
+ """
34
+ if maxlen is None:
35
+ maxlen = -1
36
+ while maxlen:
37
+ buffer = source_fobj.read(buffer_size)
38
+ if len(buffer) == 0:
39
+ break
40
+ if maxlen > 0 and maxlen < len(buffer):
41
+ buffer = buffer[:maxlen]
42
+ while len(buffer) > 0:
43
+ gevent.sleep(0)
44
+ written = destination_fobj.write(buffer)
45
+ buffer = buffer[written:]
46
+ maxlen -= written
47
+ gevent.sleep(0)
48
+
49
+
50
+ @dataclasses.dataclass
51
+ class PendingFile:
52
+ fd: IO[bytes]
53
+ filename: str
54
+
55
+
56
+ @dataclasses.dataclass
57
+ class FileWithDescription:
58
+ filename: str
59
+ description: str
60
+
61
+
62
+ class Storage(ABC):
63
+ """Abstract base class for all concrete storages."""
64
+
65
+ @abstractmethod
66
+ def get_file(self, filename: str) -> IO[bytes]:
67
+ """Retrieve a file from the storage.
68
+ filename (unicode): the path of the file to retrieve.
69
+ return (fileobj): a readable binary file-like object from which
70
+ to read the contents of the file.
71
+ raise (KeyError): if the file cannot be found.
72
+ """
73
+ pass
74
+
75
+ @abstractmethod
76
+ def create_file(self, filename: str) -> Optional[PendingFile]:
77
+ """Create an empty file that will live in the storage.
78
+ Once the caller has written the contents to the file, the commit_file()
79
+ method must be called to commit it into the store.
80
+ filename (unicode): the filename of the file to store.
81
+ return (fileobj): a writable binary file-like object on which
82
+ to write the contents of the file, or None if the file is
83
+ already stored.
84
+ """
85
+ pass
86
+
87
+ @abstractmethod
88
+ def commit_file(self, file: PendingFile, desc: str = '') -> bool:
89
+ """Commit a file created by create_file() to be stored.
90
+ Given a file object returned by create_file(), this function populates
91
+ the database to record that this file now legitimately exists and can
92
+ be used.
93
+ fobj (fileobj): the object returned by create_file()
94
+ file (PendingFile): the file to commit.
95
+ return (bool): True if the file was committed successfully, False if
96
+ there was already a file with the same filename in the database. This
97
+ shouldn't make any difference to the caller, except for testing
98
+ purposes!
99
+ """
100
+ pass
101
+
102
+ @abstractmethod
103
+ def exists(self, filename: str) -> bool:
104
+ """Check if a file exists in the storage."""
105
+ pass
106
+
107
+ @abstractmethod
108
+ def describe(self, filename: str) -> str:
109
+ """Return the description of a file given its filename.
110
+ filename (unicode): the filename of the file to describe.
111
+ return (unicode): the description of the file.
112
+ raise (KeyError): if the file cannot be found.
113
+ """
114
+ pass
115
+
116
+ @abstractmethod
117
+ def get_size(self, filename: str) -> int:
118
+ """Return the size of a file given its filename.
119
+ filename (unicode): the filename of the file to calculate the size
120
+ of.
121
+ return (int): the size of the file, in bytes.
122
+ raise (KeyError): if the file cannot be found.
123
+ """
124
+ pass
125
+
126
+ @abstractmethod
127
+ def delete(self, filename: str):
128
+ """Delete a file from the storage.
129
+ filename (unicode): the filename of the file to delete.
130
+ """
131
+ pass
132
+
133
+ @abstractmethod
134
+ def list(self) -> List[FileWithDescription]:
135
+ """List the files available in the storage.
136
+ return ([(unicode, unicode)]): a list of pairs, each
137
+ representing a file in the form (filename, description).
138
+ """
139
+ pass
140
+
141
+ @abstractmethod
142
+ def path_for_symlink(self, filename: str) -> Optional[pathlib.Path]:
143
+ pass
144
+
145
+
146
+ class NullStorage(Storage):
147
+ """This backend is always empty, it just drops each file that
148
+ receives. It looks mostly like /dev/null. It is useful when you
149
+ want to just rely on the caching capabilities of FileCacher for
150
+ very short-lived and local storages.
151
+
152
+ """
153
+
154
+ def get_file(self, digest: str) -> IO[bytes]:
155
+ raise KeyError('File not found.')
156
+
157
+ def create_file(self, digest: str) -> Optional[PendingFile]:
158
+ return None
159
+
160
+ def commit_file(self, file: PendingFile, desc: str = '') -> bool:
161
+ return False
162
+
163
+ def exists(self, filename: str) -> bool:
164
+ return False
165
+
166
+ def describe(self, digest: str) -> str:
167
+ raise KeyError('File not found.')
168
+
169
+ def get_size(self, digest: str) -> int:
170
+ raise KeyError('File not found.')
171
+
172
+ def delete(self, digest: str):
173
+ pass
174
+
175
+ def list(self) -> List[FileWithDescription]:
176
+ return list()
177
+
178
+ def path_for_symlink(self, digest: str) -> Optional[pathlib.Path]:
179
+ return None
180
+
181
+
182
+ class FilesystemStorage(Storage):
183
+ """This class implements a backend for FileCacher that keeps all
184
+ the files in a file system directory, named after their filename.
185
+ """
186
+
187
+ def __init__(self, path: pathlib.Path):
188
+ """Initialize the backend.
189
+ path (string): the base path for the storage.
190
+ """
191
+ self.path = path
192
+
193
+ # Create the directory if it doesn't exist
194
+ path.mkdir(parents=True, exist_ok=True)
195
+
196
+ def get_file(self, filename: str) -> IO[bytes]:
197
+ """See FileCacherBackend.get_file()."""
198
+ file_path = self.path / filename
199
+
200
+ if not file_path.is_file():
201
+ raise KeyError('File not found.')
202
+
203
+ return file_path.open('rb')
204
+
205
+ def create_file(self, filename: str) -> Optional[PendingFile]:
206
+ """See FileCacherBackend.create_file()."""
207
+ # Check if the file already exists. Return None if so, to inform the
208
+ # caller they don't need to store the file.
209
+ file_path = self.path / filename
210
+
211
+ if file_path.is_file():
212
+ return None
213
+
214
+ # Create a temporary file in the same directory
215
+ temp_file = tempfile.NamedTemporaryFile(
216
+ 'wb', delete=False, prefix='.tmp.', suffix=filename, dir=self.path
217
+ )
218
+ return PendingFile(fd=temp_file, filename=filename)
219
+
220
+ def commit_file(self, file: PendingFile, desc: str = '') -> bool:
221
+ """See FileCacherBackend.commit_file()."""
222
+ file.fd.close()
223
+
224
+ file_path: pathlib.Path = self.path / file.filename
225
+ # Move it into place in the cache. Skip if it already exists, and
226
+ # delete the temporary file instead.
227
+ if not file_path.is_file():
228
+ # There is a race condition here if someone else puts the file here
229
+ # between checking and renaming. Put it doesn't matter in practice,
230
+ # because rename will replace the file anyway (which should be
231
+ # identical).
232
+ pathlib.PosixPath(file.fd.name).rename(file_path)
233
+ return True
234
+ else:
235
+ pathlib.PosixPath(file.fd.name).unlink()
236
+ return False
237
+
238
+ def exists(self, filename: str) -> bool:
239
+ """See FileCacherBackend.exists()."""
240
+ file_path: pathlib.Path = self.path / filename
241
+
242
+ return file_path.is_file()
243
+
244
+ def describe(self, filename: str) -> str:
245
+ """See FileCacherBackend.describe()."""
246
+ file_path: pathlib.Path = self.path / filename
247
+
248
+ if not file_path.is_file():
249
+ raise KeyError('File not found.')
250
+
251
+ return ''
252
+
253
+ def get_size(self, filename: str) -> int:
254
+ """See FileCacherBackend.get_size()."""
255
+ file_path: pathlib.Path = self.path / filename
256
+
257
+ if not file_path.is_file():
258
+ raise KeyError('File not found.')
259
+
260
+ return file_path.stat().st_size
261
+
262
+ def delete(self, filename: str):
263
+ """See FileCacherBackend.delete()."""
264
+ file_path: pathlib.Path = self.path / filename
265
+
266
+ file_path.unlink(missing_ok=True)
267
+
268
+ def list(self) -> List[FileWithDescription]:
269
+ """See FileCacherBackend.list()."""
270
+ res = []
271
+ for path in self.path.glob('*'):
272
+ if path.is_file():
273
+ res.append(
274
+ FileWithDescription(
275
+ filename=str(path.relative_to(self.path)), description=''
276
+ )
277
+ )
278
+ return res
279
+
280
+ def path_for_symlink(self, filename: str) -> Optional[pathlib.Path]:
281
+ file_path = self.path / filename
282
+ if not file_path.is_file():
283
+ raise KeyError('File not found.')
284
+ return file_path
@@ -0,0 +1,38 @@
1
+ import atexit
2
+ import pathlib
3
+
4
+ from rich.console import Console
5
+
6
+ from rbx.grading.judge import cacher, storage
7
+ from rbx.grading.judge.sandboxes import stupid_sandbox
8
+
9
+ console = Console()
10
+
11
+
12
+ def main():
13
+ fs = storage.FilesystemStorage(pathlib.PosixPath('/tmp/rbx-storage'))
14
+ cache = cacher.FileCacher(fs)
15
+
16
+ python_file = cache.put_file_text("print('hello')")
17
+
18
+ sandbox = stupid_sandbox.StupidSandbox(cache)
19
+ atexit.register(sandbox.cleanup)
20
+ sandbox.create_file_from_storage(pathlib.PosixPath('run.py'), python_file)
21
+
22
+ sandbox.params.stdout_file = pathlib.PosixPath('run.out')
23
+
24
+ sandbox.execute_without_std(['ls'])
25
+ try:
26
+ sandbox.hydrate_logs()
27
+ except Exception:
28
+ console.print_exception()
29
+
30
+ print(sandbox.get_human_exit_description())
31
+ print(sandbox.get_stats())
32
+ print(sandbox.log)
33
+
34
+ print(sandbox.get_file_to_string(pathlib.PosixPath('run.out')))
35
+
36
+
37
+ if __name__ == '__main__':
38
+ main()
@@ -0,0 +1,54 @@
1
+ import atexit
2
+ import pathlib
3
+
4
+ from rich.console import Console
5
+
6
+ from rbx import grading_utils
7
+ from rbx.grading.judge import cacher, storage
8
+ from rbx.grading.judge.sandboxes.isolate import IsolateSandbox
9
+
10
+ console = Console()
11
+
12
+
13
+ def main():
14
+ fs = storage.FilesystemStorage(pathlib.PosixPath('/tmp/rbx-storage'))
15
+ cache = cacher.FileCacher(fs)
16
+
17
+ python_file = cache.put_file_text(
18
+ """
19
+ #include <bits/stdc++.h>
20
+
21
+ int main() {
22
+ std::cout << "Hello, World!" << std::endl;
23
+ return 0;
24
+ }
25
+ """
26
+ )
27
+
28
+ sandbox = IsolateSandbox(
29
+ cache, params=grading_utils.build_preprocess_sandbox_params(), debug=True
30
+ )
31
+ atexit.register(sandbox.cleanup)
32
+ sandbox.create_file_from_storage(pathlib.PosixPath('run.cpp'), python_file)
33
+
34
+ sandbox.params.stdout_file = pathlib.PosixPath('run.out')
35
+ sandbox.params.stderr_file = pathlib.PosixPath('run.err')
36
+
37
+ sandbox.execute_without_std(
38
+ ['/usr/bin/g++', '-std=c++17', '-o', 'executable', 'run.cpp'],
39
+ )
40
+ try:
41
+ sandbox.hydrate_logs()
42
+ except Exception:
43
+ console.print_exception()
44
+
45
+ print(sandbox.log)
46
+ print(sandbox.get_human_exit_description())
47
+ print(sandbox.get_stats())
48
+
49
+ print(sandbox.get_file_to_string(pathlib.PosixPath('run.out')))
50
+ print(sandbox.get_file_to_string(pathlib.PosixPath('run.err')))
51
+
52
+
53
+ if __name__ == '__main__':
54
+ main()