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/__init__.py ADDED
File without changes
rbx/annotations.py ADDED
@@ -0,0 +1,127 @@
1
+ import importlib
2
+ import importlib.resources
3
+ import pathlib
4
+ import re
5
+ from typing import List, Optional
6
+
7
+ import typer
8
+ import typer.core
9
+ from typing_extensions import Annotated
10
+
11
+ from rbx import config, metadata
12
+ from rbx.config import get_config
13
+
14
+
15
+ def _get_language_options():
16
+ return sorted(get_config().languages.keys())
17
+
18
+
19
+ def _get_language_default():
20
+ return get_config().defaultLanguage
21
+
22
+
23
+ def _get_problem_options():
24
+ options = set()
25
+ all_problems = metadata.find_problems()
26
+ for problem in all_problems:
27
+ options.add(problem.code)
28
+ options.update(problem.aliases)
29
+ return sorted(options)
30
+
31
+
32
+ def _list_files(path: pathlib.Path) -> List[str]:
33
+ if not path.is_dir():
34
+ return []
35
+ return [file.name for file in path.iterdir() if file.is_file()]
36
+
37
+
38
+ def _get_checker_options():
39
+ options = set()
40
+ with importlib.resources.as_file(
41
+ importlib.resources.files('rbx') / 'resources' / 'checkers'
42
+ ) as file:
43
+ options.update(_list_files(file))
44
+
45
+ options.update(_list_files(config.get_app_path() / 'checkers'))
46
+ options.remove('boilerplate.cpp')
47
+ return sorted(options)
48
+
49
+
50
+ Timelimit = Annotated[
51
+ int,
52
+ typer.Option(
53
+ '--timelimit',
54
+ '-t',
55
+ help='Time limit in milliseconds.',
56
+ prompt='Time limit (ms)',
57
+ ),
58
+ ]
59
+ Memorylimit = Annotated[
60
+ int,
61
+ typer.Option(
62
+ '--memorylimit',
63
+ '-m',
64
+ help='Memory limit in megabytes.',
65
+ prompt='Memory limit (MB)',
66
+ ),
67
+ ]
68
+ Multitest = Annotated[
69
+ Optional[bool],
70
+ typer.Option(
71
+ '--multitest',
72
+ '-m',
73
+ is_flag=True,
74
+ help='Whether this problem have multiple tests per file.',
75
+ prompt='Multitest?',
76
+ ),
77
+ ]
78
+ Language = Annotated[
79
+ str,
80
+ typer.Option(
81
+ '--language',
82
+ '--lang',
83
+ '-l',
84
+ help='Language to use.',
85
+ prompt='Language',
86
+ default_factory=_get_language_default,
87
+ autocompletion=_get_language_options,
88
+ ),
89
+ ]
90
+ LanguageWithDefault = Annotated[
91
+ Optional[str],
92
+ typer.Option(
93
+ '--language',
94
+ '--lang',
95
+ '-l',
96
+ help='Language to use.',
97
+ autocompletion=_get_language_options,
98
+ ),
99
+ ]
100
+ Problem = Annotated[str, typer.Argument(autocompletion=_get_problem_options)]
101
+
102
+ ProblemOption = Annotated[
103
+ Optional[str], typer.Option('--problem', '-p', autocompletion=_get_problem_options)
104
+ ]
105
+
106
+ TestcaseIndex = Annotated[Optional[int], typer.Option('--index', '--idx', '-i')]
107
+
108
+ Checker = Annotated[
109
+ str,
110
+ typer.Argument(
111
+ autocompletion=_get_checker_options, help='Path to a testlib checker file.'
112
+ ),
113
+ ]
114
+
115
+
116
+ class AliasGroup(typer.core.TyperGroup):
117
+ _CMD_SPLIT_P = re.compile(r', ?')
118
+
119
+ def get_command(self, ctx, cmd_name):
120
+ cmd_name = self._group_cmd_name(cmd_name)
121
+ return super().get_command(ctx, cmd_name)
122
+
123
+ def _group_cmd_name(self, default_name):
124
+ for cmd in self.commands.values():
125
+ if cmd.name and default_name in self._CMD_SPLIT_P.split(cmd.name):
126
+ return cmd.name
127
+ return default_name
rbx/autoenum.py ADDED
@@ -0,0 +1,333 @@
1
+ # type: ignore
2
+ from enum import Enum, auto
3
+ from typing import Any, Dict, List, Optional, Set, Tuple, Union
4
+
5
+
6
+ class alias(auto):
7
+ def __init__(self, *aliases):
8
+ if len(aliases) == 0:
9
+ raise ValueError('Cannot have empty alias() call.')
10
+ for a in aliases:
11
+ if not isinstance(a, str):
12
+ raise ValueError(
13
+ f'All aliases for must be strings; found alias of type {type(a)} having value: {a}'
14
+ )
15
+ self.names = aliases
16
+ self.enum_name = None
17
+
18
+ def __repr__(self) -> str:
19
+ return str(self)
20
+
21
+ def __str__(self):
22
+ if self.enum_name is not None:
23
+ return self.enum_name
24
+ return self.alias_repr
25
+
26
+ @property
27
+ def alias_repr(self) -> str:
28
+ return str(f'alias:{list(self.names)}')
29
+
30
+ def __setattr__(self, attr_name: str, attr_value: Any):
31
+ if attr_name == 'value':
32
+ ## because alias subclasses auto and does not set value, enum.py:143 will try to set value
33
+ self.enum_name = attr_value
34
+ else:
35
+ super(alias, self).__setattr__(attr_name, attr_value)
36
+
37
+ def __getattribute__(self, attr_name: str):
38
+ """
39
+ Refer these lines in Python 3.10.9 enum.py:
40
+
41
+ class _EnumDict(dict):
42
+ ...
43
+ def __setitem__(self, key, value):
44
+ ...
45
+ elif not _is_descriptor(value):
46
+ ...
47
+ if isinstance(value, auto):
48
+ if value.value == _auto_null:
49
+ value.value = self._generate_next_value(
50
+ key,
51
+ 1,
52
+ len(self._member_names),
53
+ self._last_values[:],
54
+ )
55
+ self._auto_called = True
56
+ value = value.value
57
+ ...
58
+ ...
59
+ ...
60
+
61
+ """
62
+ if attr_name == 'value':
63
+ if object.__getattribute__(self, 'enum_name') is None:
64
+ ## Gets _auto_null as alias inherits auto class but does not set `value` class member; refer enum.py:142
65
+ try:
66
+ return object.__getattribute__(self, 'value')
67
+ except Exception:
68
+ from enum import _auto_null
69
+
70
+ return _auto_null
71
+ return self
72
+ return object.__getattribute__(self, attr_name)
73
+
74
+
75
+ _DEFAULT_REMOVAL_TABLE = str.maketrans(
76
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
77
+ 'abcdefghijklmnopqrstuvwxyz',
78
+ ' -_.:;,', ## Will be removed
79
+ )
80
+
81
+
82
+ class AutoEnum(str, Enum):
83
+ """
84
+ Utility class which can be subclassed to create enums using auto() and alias().
85
+ Also provides utility methods for common enum operations.
86
+ """
87
+
88
+ def __init__(self, value: Union[str, alias]):
89
+ self.aliases: Tuple[str, ...] = tuple()
90
+ if isinstance(value, alias):
91
+ self.aliases: Tuple[str, ...] = value.names
92
+
93
+ @classmethod
94
+ def _missing_(cls, enum_value: Any):
95
+ ## Ref: https://stackoverflow.com/a/60174274/4900327
96
+ ## This is needed to allow Pydantic to perform case-insensitive conversion to AutoEnum.
97
+ return cls.from_str(enum_value=enum_value, raise_error=True)
98
+
99
+ def _generate_next_value_(name, start, count, last_values):
100
+ return name
101
+
102
+ @property
103
+ def str(self) -> str:
104
+ return self.__str__()
105
+
106
+ def __repr__(self):
107
+ return self.__str__()
108
+
109
+ def __str__(self):
110
+ return self.name
111
+
112
+ def __hash__(self):
113
+ return hash(self.__class__.__name__ + '.' + self.name)
114
+
115
+ def __eq__(self, other):
116
+ return self is other
117
+
118
+ def __ne__(self, other):
119
+ return self is not other
120
+
121
+ def matches(self, enum_value: str) -> bool:
122
+ return self is self.from_str(enum_value, raise_error=False)
123
+
124
+ @classmethod
125
+ def matches_any(cls, enum_value: str) -> bool:
126
+ return cls.from_str(enum_value, raise_error=False) is not None
127
+
128
+ @classmethod
129
+ def does_not_match_any(cls, enum_value: str) -> bool:
130
+ return not cls.matches_any(enum_value)
131
+
132
+ @classmethod
133
+ def display_names(cls, **kwargd) -> str:
134
+ return str([enum_value.display_name(**kwargd) for enum_value in list(cls)])
135
+
136
+ def display_name(self, *, sep: str = ' ') -> str:
137
+ return sep.join(
138
+ [
139
+ (
140
+ word.lower()
141
+ if word.lower() in ('of', 'in', 'the')
142
+ else word.capitalize()
143
+ )
144
+ for word in str(self).split('_')
145
+ ]
146
+ )
147
+
148
+ @classmethod
149
+ def _initialize_lookup(cls):
150
+ if (
151
+ '_value2member_map_normalized_' not in cls.__dict__
152
+ ): ## Caching values for fast retrieval.
153
+ cls._value2member_map_normalized_ = {}
154
+
155
+ def _set_normalized(e, normalized_e_name):
156
+ if normalized_e_name in cls._value2member_map_normalized_:
157
+ print('DEU MERDA')
158
+ raise ValueError(
159
+ f'Cannot register enum "{e.name}"; '
160
+ f'another enum with the same normalized name "{normalized_e_name}" already exists.'
161
+ )
162
+ cls._value2member_map_normalized_[normalized_e_name] = e
163
+
164
+ for e in list(cls):
165
+ _set_normalized(e, cls._normalize(e.name))
166
+ if len(e.aliases) > 0:
167
+ ## Add the alias-repr to the lookup:
168
+ _set_normalized(e, cls._normalize(alias(*e.aliases).alias_repr))
169
+ candidate_aliases = set(
170
+ cls._normalize(e_alias) for e_alias in e.aliases
171
+ )
172
+ if cls._normalize(e.name) in candidate_aliases:
173
+ candidate_aliases.remove(cls._normalize(e.name))
174
+ for e_alias in candidate_aliases:
175
+ _set_normalized(e, e_alias)
176
+
177
+ @classmethod
178
+ def from_str(cls, enum_value: str, raise_error: bool = True) -> Optional:
179
+ """
180
+ Performs a case-insensitive lookup of the enum value string among the members of the current AutoEnum subclass.
181
+ :param enum_value: enum value string
182
+ :param raise_error: whether to raise an error if the string is not found in the enum
183
+ :return: an enum value which matches the string
184
+ :raises: ValueError if raise_error is True and no enum value matches the string
185
+ """
186
+ if isinstance(enum_value, cls):
187
+ return enum_value
188
+ if enum_value is None and raise_error is False:
189
+ return None
190
+ if not isinstance(enum_value, str) and raise_error is True:
191
+ raise ValueError(f'Input should be a string; found type {type(enum_value)}')
192
+ cls._initialize_lookup()
193
+ enum_obj: Optional[AutoEnum] = cls._value2member_map_normalized_.get(
194
+ cls._normalize(enum_value)
195
+ )
196
+ if enum_obj is None and raise_error is True:
197
+ raise ValueError(
198
+ f'Could not find enum with value {repr(enum_value)}; available values are: {list(cls)}.'
199
+ )
200
+ return enum_obj
201
+
202
+ @classmethod
203
+ def _normalize(cls, x: str) -> str:
204
+ ## Found to be faster than .translate() and re.sub() on Python 3.10.6
205
+ return str(x).translate(_DEFAULT_REMOVAL_TABLE)
206
+
207
+ @classmethod
208
+ def convert_keys(cls, d: Dict) -> Dict:
209
+ """
210
+ Converts string dict keys to the matching members of the current AutoEnum subclass.
211
+ Leaves non-string keys untouched.
212
+ :param d: dict to transform
213
+ :return: dict with matching string keys transformed to enum values
214
+ """
215
+ out_dict = {}
216
+ for k, v in d.items():
217
+ if isinstance(k, str) and cls.from_str(k, raise_error=False) is not None:
218
+ out_dict[cls.from_str(k, raise_error=False)] = v
219
+ else:
220
+ out_dict[k] = v
221
+ return out_dict
222
+
223
+ @classmethod
224
+ def convert_keys_to_str(cls, d: Dict) -> Dict:
225
+ """
226
+ Converts dict keys of the current AutoEnum subclass to the matching string key.
227
+ Leaves other keys untouched.
228
+ :param d: dict to transform
229
+ :return: dict with matching keys of the current AutoEnum transformed to strings.
230
+ """
231
+ out_dict = {}
232
+ for k, v in d.items():
233
+ if isinstance(k, cls):
234
+ out_dict[str(k)] = v
235
+ else:
236
+ out_dict[k] = v
237
+ return out_dict
238
+
239
+ @classmethod
240
+ def convert_values(
241
+ cls, d: Union[Dict, Set, List, Tuple], raise_error: bool = False
242
+ ) -> Union[Dict, Set, List, Tuple]:
243
+ """
244
+ Converts string values to the matching members of the current AutoEnum subclass.
245
+ Leaves non-string values untouched.
246
+ :param d: dict, set, list or tuple to transform.
247
+ :param raise_error: raise an error if unsupported type.
248
+ :return: data structure with matching string values transformed to enum values.
249
+ """
250
+ if isinstance(d, dict):
251
+ return cls.convert_dict_values(d)
252
+ if isinstance(d, list):
253
+ return cls.convert_list(d)
254
+ if isinstance(d, tuple):
255
+ return tuple(cls.convert_list(d))
256
+ if isinstance(d, set):
257
+ return cls.convert_set(d)
258
+ if raise_error:
259
+ raise ValueError(f'Unrecognized data structure of type {type(d)}')
260
+ return d
261
+
262
+ @classmethod
263
+ def convert_dict_values(cls, d: Dict) -> Dict:
264
+ """
265
+ Converts string dict values to the matching members of the current AutoEnum subclass.
266
+ Leaves non-string values untouched.
267
+ :param d: dict to transform
268
+ :return: dict with matching string values transformed to enum values
269
+ """
270
+ out_dict = {}
271
+ for k, v in d.items():
272
+ if isinstance(v, str) and cls.from_str(v, raise_error=False) is not None:
273
+ out_dict[k] = cls.from_str(v, raise_error=False)
274
+ else:
275
+ out_dict[k] = v
276
+ return out_dict
277
+
278
+ @classmethod
279
+ def convert_list(cls, ls: Union[List, Tuple]) -> List:
280
+ """
281
+ Converts string list itmes to the matching members of the current AutoEnum subclass.
282
+ Leaves non-string items untouched.
283
+ :param l: list to transform
284
+ :return: list with matching string items transformed to enum values
285
+ """
286
+ out_list = []
287
+ for item in ls:
288
+ if isinstance(item, str) and cls.matches_any(item):
289
+ out_list.append(cls.from_str(item))
290
+ else:
291
+ out_list.append(item)
292
+ return out_list
293
+
294
+ @classmethod
295
+ def convert_set(cls, s: Set) -> Set:
296
+ """
297
+ Converts string list itmes to the matching members of the current AutoEnum subclass.
298
+ Leaves non-string items untouched.
299
+ :param s: set to transform
300
+ :return: set with matching string items transformed to enum values
301
+ """
302
+ out_set = set()
303
+ for item in s:
304
+ if isinstance(item, str) and cls.matches_any(item):
305
+ out_set.add(cls.from_str(item))
306
+ else:
307
+ out_set.add(item)
308
+ return out_set
309
+
310
+ @classmethod
311
+ def convert_values_to_str(cls, d: Dict) -> Dict:
312
+ """
313
+ Converts dict values of the current AutoEnum subclass to the matching string value.
314
+ Leaves other values untouched.
315
+ :param d: dict to transform
316
+ :return: dict with matching values of the current AutoEnum transformed to strings.
317
+ """
318
+ out_dict = {}
319
+ for k, v in d.items():
320
+ if isinstance(v, cls):
321
+ out_dict[k] = str(v)
322
+ else:
323
+ out_dict[k] = v
324
+ return out_dict
325
+
326
+
327
+ class TestEnum(AutoEnum):
328
+ ACCEPTED = alias('accepted', 'ac')
329
+ WRONG_ANSWER = alias('wrong answer', 'wa')
330
+
331
+
332
+ if __name__ == '__main__':
333
+ print(TestEnum('ac'))
rbx/box/__init__.py ADDED
File without changes
rbx/box/builder.py ADDED
@@ -0,0 +1,77 @@
1
+ from typing import Optional, Set
2
+
3
+ from rbx import console, utils
4
+ from rbx.box import environment, package
5
+ from rbx.box.environment import VerificationLevel
6
+ from rbx.box.generators import generate_outputs_for_testcases, generate_testcases
7
+ from rbx.box.solutions import (
8
+ is_fast,
9
+ print_run_report,
10
+ run_solutions,
11
+ )
12
+ from rbx.box.validators import print_validation_report, validate_testcases
13
+
14
+
15
+ def build(
16
+ verification: environment.VerificationParam,
17
+ groups: Optional[Set[str]] = None,
18
+ output: bool = True,
19
+ ) -> None:
20
+ with utils.StatusProgress(
21
+ 'Building testcases...',
22
+ 'Built [item]{processed}[/item] testcases...',
23
+ keep=True,
24
+ ) as s:
25
+ generate_testcases(s, groups=groups)
26
+
27
+ with utils.StatusProgress(
28
+ 'Building outputs for testcases...',
29
+ 'Built [item]{processed}[/item] outputs...',
30
+ keep=True,
31
+ ) as s:
32
+ if output:
33
+ generate_outputs_for_testcases(s, groups=groups)
34
+
35
+ if verification > 0:
36
+ with utils.StatusProgress(
37
+ 'Validating testcases...',
38
+ 'Validated [item]{processed}[/item] testcases...',
39
+ keep=True,
40
+ ) as s:
41
+ infos = validate_testcases(s, groups=groups)
42
+ print_validation_report(infos)
43
+
44
+ console.console.print(
45
+ '[success]Problem built.[/success] '
46
+ '[warning]Check the output for verification errors![/warning]'
47
+ )
48
+
49
+
50
+ def verify(verification: environment.VerificationParam) -> bool:
51
+ build(verification=verification)
52
+
53
+ if verification < VerificationLevel.FAST_SOLUTIONS.value:
54
+ return True
55
+
56
+ tracked_solutions = None
57
+ if verification < VerificationLevel.ALL_SOLUTIONS.value:
58
+ pkg = package.find_problem_package_or_die()
59
+
60
+ tracked_solutions = {
61
+ str(solution.path) for solution in pkg.solutions if is_fast(solution)
62
+ }
63
+
64
+ with utils.StatusProgress('Running solutions...') as s:
65
+ solution_result = run_solutions(
66
+ progress=s,
67
+ tracked_solutions=tracked_solutions,
68
+ verification=VerificationLevel(verification),
69
+ )
70
+
71
+ console.console.print()
72
+ console.console.rule('[status]Run report[/status]', style='status')
73
+ return print_run_report(
74
+ solution_result,
75
+ console.console,
76
+ verification,
77
+ )
rbx/box/cd.py ADDED
@@ -0,0 +1,37 @@
1
+ import functools
2
+ import pathlib
3
+ from typing import Optional
4
+
5
+ import typer
6
+ from rich import console
7
+
8
+ from rbx import utils
9
+
10
+
11
+ def find_package(root: pathlib.Path = pathlib.Path()) -> Optional[pathlib.Path]:
12
+ root = root.resolve()
13
+
14
+ def has_file():
15
+ problem_yaml_path = root / 'problem.rbx.yml'
16
+ contest_yaml_path = root / 'contest.rbx.yml'
17
+ return problem_yaml_path.is_file() or contest_yaml_path.is_file()
18
+
19
+ while root != pathlib.PosixPath('/') and not has_file():
20
+ root = root.parent
21
+ if not has_file():
22
+ return None
23
+ return root
24
+
25
+
26
+ def within_closest_package(func):
27
+ @functools.wraps(func)
28
+ def wrapper(*args, **kwargs):
29
+ package = find_package()
30
+ if package is None:
31
+ console.console.print('[error]No rbx package found.[/error]')
32
+ raise typer.Exit(1)
33
+ # Get deepest package.
34
+ with utils.new_cd(package):
35
+ return func(*args, **kwargs)
36
+
37
+ return wrapper