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.
- rbx/__init__.py +0 -0
- rbx/annotations.py +127 -0
- rbx/autoenum.py +333 -0
- rbx/box/__init__.py +0 -0
- rbx/box/builder.py +77 -0
- rbx/box/cd.py +37 -0
- rbx/box/checkers.py +134 -0
- rbx/box/code.py +185 -0
- rbx/box/compile.py +56 -0
- rbx/box/conftest.py +42 -0
- rbx/box/contest/__init__.py +0 -0
- rbx/box/contest/build_contest_statements.py +347 -0
- rbx/box/contest/contest_package.py +76 -0
- rbx/box/contest/contest_utils.py +20 -0
- rbx/box/contest/main.py +179 -0
- rbx/box/contest/schema.py +155 -0
- rbx/box/contest/statements.py +82 -0
- rbx/box/creation.py +72 -0
- rbx/box/download.py +64 -0
- rbx/box/environment.py +345 -0
- rbx/box/extensions.py +26 -0
- rbx/box/generators.py +478 -0
- rbx/box/generators_test.py +63 -0
- rbx/box/main.py +449 -0
- rbx/box/package.py +316 -0
- rbx/box/packaging/boca/extension.py +27 -0
- rbx/box/packaging/boca/packager.py +245 -0
- rbx/box/packaging/contest_main.py +82 -0
- rbx/box/packaging/main.py +68 -0
- rbx/box/packaging/packager.py +117 -0
- rbx/box/packaging/polygon/packager.py +320 -0
- rbx/box/packaging/polygon/test.py +81 -0
- rbx/box/packaging/polygon/xml_schema.py +106 -0
- rbx/box/presets/__init__.py +503 -0
- rbx/box/presets/fetch.py +70 -0
- rbx/box/presets/lock_schema.py +20 -0
- rbx/box/presets/schema.py +59 -0
- rbx/box/schema.py +394 -0
- rbx/box/solutions.py +792 -0
- rbx/box/solutions_test.py +41 -0
- rbx/box/statements/__init__.py +0 -0
- rbx/box/statements/build_statements.py +359 -0
- rbx/box/statements/builders.py +375 -0
- rbx/box/statements/joiners.py +113 -0
- rbx/box/statements/latex.py +47 -0
- rbx/box/statements/latex_jinja.py +214 -0
- rbx/box/statements/schema.py +138 -0
- rbx/box/stresses.py +292 -0
- rbx/box/stressing/__init__.py +0 -0
- rbx/box/stressing/finder_parser.py +359 -0
- rbx/box/stressing/generator_parser.py +258 -0
- rbx/box/testcases.py +54 -0
- rbx/box/ui/__init__.py +0 -0
- rbx/box/ui/captured_log.py +372 -0
- rbx/box/ui/css/app.tcss +48 -0
- rbx/box/ui/main.py +38 -0
- rbx/box/ui/run.py +209 -0
- rbx/box/validators.py +245 -0
- rbx/box/validators_test.py +15 -0
- rbx/checker.py +128 -0
- rbx/clone.py +197 -0
- rbx/config.py +271 -0
- rbx/conftest.py +38 -0
- rbx/console.py +27 -0
- rbx/create.py +37 -0
- rbx/edit.py +24 -0
- rbx/grading/__init__.py +0 -0
- rbx/grading/caching.py +356 -0
- rbx/grading/conftest.py +33 -0
- rbx/grading/judge/__init__.py +0 -0
- rbx/grading/judge/cacher.py +503 -0
- rbx/grading/judge/digester.py +35 -0
- rbx/grading/judge/sandbox.py +748 -0
- rbx/grading/judge/sandboxes/__init__.py +0 -0
- rbx/grading/judge/sandboxes/isolate.py +683 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +310 -0
- rbx/grading/judge/sandboxes/timeit.py +217 -0
- rbx/grading/judge/storage.py +284 -0
- rbx/grading/judge/test.py +38 -0
- rbx/grading/judge/testiso.py +54 -0
- rbx/grading/steps.py +522 -0
- rbx/grading/steps_with_caching.py +59 -0
- rbx/grading/steps_with_caching_run_test.py +429 -0
- rbx/grading_utils.py +148 -0
- rbx/hydration.py +101 -0
- rbx/main.py +122 -0
- rbx/metadata.py +105 -0
- rbx/providers/__init__.py +43 -0
- rbx/providers/codeforces.py +73 -0
- rbx/providers/provider.py +26 -0
- rbx/resources/checkers/boilerplate.cpp +20 -0
- rbx/resources/default_config.json +48 -0
- rbx/resources/envs/default.rbx.yml +37 -0
- rbx/resources/envs/isolate.rbx.yml +37 -0
- rbx/resources/packagers/boca/checker.sh +43 -0
- rbx/resources/packagers/boca/compare +53 -0
- rbx/resources/packagers/boca/compile/c +172 -0
- rbx/resources/packagers/boca/compile/cc +173 -0
- rbx/resources/packagers/boca/compile/cpp +172 -0
- rbx/resources/packagers/boca/compile/java +194 -0
- rbx/resources/packagers/boca/compile/kt +155 -0
- rbx/resources/packagers/boca/compile/pas +172 -0
- rbx/resources/packagers/boca/compile/py2 +173 -0
- rbx/resources/packagers/boca/compile/py3 +173 -0
- rbx/resources/packagers/boca/run/c +128 -0
- rbx/resources/packagers/boca/run/cc +128 -0
- rbx/resources/packagers/boca/run/cpp +128 -0
- rbx/resources/packagers/boca/run/java +194 -0
- rbx/resources/packagers/boca/run/kt +159 -0
- rbx/resources/packagers/boca/run/py2 +166 -0
- rbx/resources/packagers/boca/run/py3 +166 -0
- rbx/resources/presets/default/contest/contest.rbx.yml +14 -0
- rbx/resources/presets/default/contest/statement/contest.rbx.tex +97 -0
- rbx/resources/presets/default/contest/statement/olymp.sty +250 -0
- rbx/resources/presets/default/contest/statement/template.rbx.tex +42 -0
- rbx/resources/presets/default/preset.rbx.yml +12 -0
- rbx/resources/presets/default/problem/.gitignore +6 -0
- rbx/resources/presets/default/problem/gen.cpp +9 -0
- rbx/resources/presets/default/problem/problem.rbx.yml +44 -0
- rbx/resources/presets/default/problem/random.py +3 -0
- rbx/resources/presets/default/problem/random.txt +2 -0
- rbx/resources/presets/default/problem/sols/main.cpp +9 -0
- rbx/resources/presets/default/problem/sols/slow.cpp +15 -0
- rbx/resources/presets/default/problem/sols/wa.cpp +9 -0
- rbx/resources/presets/default/problem/statement/olymp.sty +250 -0
- rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- rbx/resources/presets/default/problem/statement/statement.rbx.tex +18 -0
- rbx/resources/presets/default/problem/statement/template.rbx.tex +89 -0
- rbx/resources/presets/default/problem/tests/samples/000.in +1 -0
- rbx/resources/presets/default/problem/tests/samples/001.in +1 -0
- rbx/resources/presets/default/problem/validator.cpp +16 -0
- rbx/resources/presets/default/problem/wcmp.cpp +34 -0
- rbx/resources/templates/template.cpp +19 -0
- rbx/run.py +45 -0
- rbx/schema.py +64 -0
- rbx/submit.py +61 -0
- rbx/submitors/__init__.py +18 -0
- rbx/submitors/codeforces.py +120 -0
- rbx/submitors/submitor.py +25 -0
- rbx/test.py +347 -0
- rbx/testcase.py +70 -0
- rbx/testcase_rendering.py +79 -0
- rbx/testdata/box1/gen1.cpp +7 -0
- rbx/testdata/box1/gen2.cpp +9 -0
- rbx/testdata/box1/genScript.py +2 -0
- rbx/testdata/box1/hard-tle.sol.cpp +26 -0
- rbx/testdata/box1/ole.cpp +17 -0
- rbx/testdata/box1/problem.rbx.yml +39 -0
- rbx/testdata/box1/re.sol.cpp +23 -0
- rbx/testdata/box1/sol.cpp +22 -0
- rbx/testdata/box1/tests/1.in +1 -0
- rbx/testdata/box1/tle-and-incorrect.sol.cpp +33 -0
- rbx/testdata/box1/tle.sol.cpp +35 -0
- rbx/testdata/box1/validator.cpp +11 -0
- rbx/testdata/box1/wa.sol.cpp +22 -0
- rbx/testdata/caching/executable.py +1 -0
- rbx/testdata/compatible +0 -0
- rbx/testing_utils.py +65 -0
- rbx/utils.py +162 -0
- rbx_cp-0.5.0.dist-info/LICENSE +201 -0
- rbx_cp-0.5.0.dist-info/METADATA +89 -0
- rbx_cp-0.5.0.dist-info/RECORD +164 -0
- rbx_cp-0.5.0.dist-info/WHEEL +4 -0
- 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
|