rbx.cp 0.11.2__py3-none-any.whl → 0.12.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/box/builder.py CHANGED
@@ -96,10 +96,10 @@ async def verify(verification: environment.VerificationParam) -> bool:
96
96
 
97
97
  tracked_solutions = None
98
98
  if verification < VerificationLevel.ALL_SOLUTIONS.value:
99
- pkg = package.find_problem_package_or_die()
100
-
101
99
  tracked_solutions = {
102
- str(solution.path) for solution in pkg.solutions if is_fast(solution)
100
+ str(solution.path)
101
+ for solution in package.get_solutions()
102
+ if is_fast(solution)
103
103
  }
104
104
 
105
105
  with utils.StatusProgress('Running solutions...') as s:
@@ -1,6 +1,5 @@
1
1
  import dataclasses
2
2
  import pathlib
3
- import subprocess
4
3
  import tempfile
5
4
  import typing
6
5
  from typing import Any, Dict, List, Optional, Tuple
@@ -13,7 +12,7 @@ from rbx.box.contest.contest_package import get_problems
13
12
  from rbx.box.contest.schema import Contest, ContestProblem, ContestStatement
14
13
  from rbx.box.formatting import href
15
14
  from rbx.box.schema import Package, Testcase
16
- from rbx.box.statements import build_statements
15
+ from rbx.box.statements import build_statements, latex
17
16
  from rbx.box.statements.build_statements import (
18
17
  get_builders,
19
18
  get_environment_languages_for_statement,
@@ -253,10 +252,10 @@ def build_contest_only(
253
252
  console.console.log(
254
253
  f'Installing LaTeX packages for [item]{statement.name} {statement.language}[/item]...'
255
254
  )
256
- subprocess.run(
257
- ['texliveonfly', output],
258
- cwd=td,
259
- )
255
+ tmp_file = pathlib.Path(td) / '__tmp_install__.tex'
256
+ tmp_file.write_bytes(output)
257
+ latex.install_tex_packages(tmp_file, pathlib.Path(td))
258
+
260
259
  last_content = output
261
260
  last_output = bdr.output_type()
262
261
 
@@ -49,7 +49,6 @@ async def build(
49
49
  vars: Annotated[
50
50
  Optional[List[str]],
51
51
  typer.Option(
52
- '-v',
53
52
  '--vars',
54
53
  help='Variables to be used in the statements.',
55
54
  ),
rbx/box/generators.py CHANGED
@@ -1,3 +1,4 @@
1
+ import functools
1
2
  import pathlib
2
3
  import shutil
3
4
  from typing import Dict, List, Optional, Set
@@ -40,12 +41,44 @@ def _compile_generator(generator: CodeItem) -> str:
40
41
  return compile_item(generator, sanitized=SanitizationLevel.PREFER)
41
42
 
42
43
 
44
+ @functools.cache
45
+ def _warn_once_about_crlf():
46
+ console.console.print(
47
+ '[warning]It seems a few files have CRLF (\\r\\n) line endings.[/warning]'
48
+ )
49
+ console.console.print(
50
+ '[warning]This usually happens when the file is created on Windows. Please convert the file to LF (\\n) line endings.[/warning]'
51
+ )
52
+ console.console.print(
53
+ '[warning]If you are in VSCode, you can make sure LF (\\n) line endings are used by changing the [item]"files.eol"[/item] setting.[/warning]'
54
+ )
55
+
56
+
57
+ @functools.cache
58
+ def _warn_about_crlf(path: pathlib.Path):
59
+ _warn_once_about_crlf()
60
+ console.console.print(
61
+ f'[warning]Testcase file [item]{path}[/item] has CRLF (\\r\\n) line endings, converting to LF (\\n).[/warning]'
62
+ )
63
+
64
+
65
+ def _check_crlf(path: pathlib.Path):
66
+ with open(path, 'rb') as f:
67
+ for line in f:
68
+ if line.endswith(b'\r\n') or line.endswith(b'\n\r'):
69
+ _warn_about_crlf(path)
70
+ break
71
+
72
+ path.write_text(path.read_text().replace('\r', ''))
73
+
74
+
43
75
  def _copy_testcase_over(
44
76
  testcase: Testcase,
45
77
  dest: Testcase,
46
78
  ):
47
79
  testcase = fill_output_for_defined_testcase(testcase)
48
80
  dest.inputPath.parent.mkdir(parents=True, exist_ok=True)
81
+ _check_crlf(testcase.inputPath)
49
82
  shutil.copy(
50
83
  str(testcase.inputPath),
51
84
  str(dest.inputPath),
@@ -55,6 +88,7 @@ def _copy_testcase_over(
55
88
  and testcase.outputPath.is_file()
56
89
  and dest.outputPath is not None
57
90
  ):
91
+ _check_crlf(testcase.outputPath)
58
92
  dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
59
93
  shutil.copy(
60
94
  str(testcase.outputPath),
@@ -71,6 +105,8 @@ def _copy_testcase_output_over(
71
105
  if not src_path.is_file():
72
106
  return False
73
107
 
108
+ _check_crlf(src_path)
109
+
74
110
  shutil.copy(str(src_path), str(dest_output_path.with_suffix(suffix)))
75
111
  return True
76
112
 
@@ -84,6 +120,7 @@ def _copy_testcase_outputs_over(
84
120
  has_copied = False
85
121
 
86
122
  if testcase.outputPath is not None and testcase.outputPath.is_file():
123
+ _check_crlf(testcase.outputPath)
87
124
  shutil.copy(str(testcase.outputPath), str(dest.outputPath))
88
125
  has_copied = True
89
126
 
@@ -120,35 +157,42 @@ def get_call_from_string(call_str: str) -> GeneratorCall:
120
157
  async def _get_necessary_generators_for_groups(
121
158
  groups: Optional[Set[str]] = None,
122
159
  ) -> Set[str]:
123
- pkg = package.find_problem_package_or_die()
124
- existing_generators = set(generator.name for generator in pkg.generators)
125
160
  necessary_generators = set()
126
161
 
127
162
  class NecessaryGeneratorsVisitor(TestcaseGroupVisitor):
128
163
  async def visit(self, entry: GenerationTestcaseEntry):
129
164
  if entry.metadata.generator_call is not None:
165
+ if (
166
+ package.get_generator_or_nil(entry.metadata.generator_call.name)
167
+ is None
168
+ ):
169
+ console.console.print(
170
+ f'[error]Generator [item]{entry.metadata.generator_call.name}[/item] is not present in the package.[/error]'
171
+ )
172
+ if entry.metadata.generator_script is not None:
173
+ console.console.print(
174
+ f'[error]This generator is referenced from [item]{entry.metadata.generator_script}[/item].[/error]'
175
+ )
176
+ raise typer.Exit(1)
130
177
  necessary_generators.add(entry.metadata.generator_call.name)
131
178
 
132
179
  await run_testcase_visitor(NecessaryGeneratorsVisitor(groups))
133
180
 
134
- return existing_generators.intersection(necessary_generators)
181
+ return necessary_generators
135
182
 
136
183
 
137
184
  def compile_generators(
185
+ tracked_generators: Set[str],
138
186
  progress: Optional[StatusProgress] = None,
139
- tracked_generators: Optional[Set[str]] = None,
140
187
  ) -> Dict[str, str]:
141
188
  def update_status(text: str):
142
189
  if progress is not None:
143
190
  progress.update(text)
144
191
 
145
- pkg = package.find_problem_package_or_die()
146
-
147
192
  generator_to_compiled_digest = {}
148
193
 
149
- for generator in pkg.generators:
150
- if tracked_generators is not None and generator.name not in tracked_generators:
151
- continue
194
+ for generator_name in tracked_generators:
195
+ generator = package.get_generator(generator_name)
152
196
  update_status(f'Compiling generator [item]{generator.name}[/item]')
153
197
  try:
154
198
  generator_to_compiled_digest[generator.name] = _compile_generator(generator)
@@ -267,9 +311,7 @@ async def generate_testcases(
267
311
 
268
312
  compiled_generators = compile_generators(
269
313
  progress=progress,
270
- tracked_generators=await _get_necessary_generators_for_groups(groups)
271
- if groups is not None
272
- else None,
314
+ tracked_generators=await _get_necessary_generators_for_groups(groups),
273
315
  )
274
316
 
275
317
  testcase_utils.clear_built_testcases()
rbx/box/package.py CHANGED
@@ -229,13 +229,45 @@ def get_build_testgroup_path(
229
229
 
230
230
 
231
231
  @functools.cache
232
- def get_generator(name: str, root: pathlib.Path = pathlib.Path()) -> Generator:
232
+ def get_generator_or_nil(
233
+ name: str, root: pathlib.Path = pathlib.Path()
234
+ ) -> Optional[Generator]:
233
235
  package = find_problem_package_or_die(root)
234
236
  for generator in package.generators:
235
237
  if generator.name == name:
236
238
  return generator
237
- console.console.print(f'[error]Generator [item]{name}[/item] not found[/error]')
238
- raise typer.Exit(1)
239
+
240
+ path = pathlib.Path(root / name)
241
+ if path.is_file():
242
+ return Generator(name=name, path=path)
243
+
244
+ path_pattern = path.with_suffix('.*')
245
+ matching_files = list(
246
+ file.relative_to(root) for file in root.glob(str(path_pattern))
247
+ )
248
+
249
+ if len(matching_files) > 1:
250
+ console.console.print(
251
+ f'[error]Multiple candidate generators found for [item]{name}[/item]: {matching_files}[/error]'
252
+ )
253
+ console.console.print(
254
+ '[info]Please specify the generator path explicitly, including the extension, or rename the conflicting files.[/info]'
255
+ )
256
+ raise typer.Exit(1)
257
+
258
+ if matching_files:
259
+ return Generator(name=name, path=matching_files[0])
260
+
261
+ return None
262
+
263
+
264
+ @functools.cache
265
+ def get_generator(name: str, root: pathlib.Path = pathlib.Path()) -> Generator:
266
+ generator = get_generator_or_nil(name, root)
267
+ if generator is None:
268
+ console.console.print(f'[error]Generator [item]{name}[/item] not found[/error]')
269
+ raise typer.Exit(1)
270
+ return generator
239
271
 
240
272
 
241
273
  @functools.cache
@@ -292,7 +324,27 @@ def get_interactor(root: pathlib.Path = pathlib.Path()) -> CodeItem:
292
324
  @functools.cache
293
325
  def get_solutions(root: pathlib.Path = pathlib.Path()) -> List[Solution]:
294
326
  package = find_problem_package_or_die(root)
295
- return package.solutions
327
+ seen_paths = set()
328
+ res = []
329
+
330
+ def add_solution(entry: Solution):
331
+ if entry.path in seen_paths:
332
+ return
333
+ seen_paths.add(entry.path)
334
+ res.append(entry)
335
+
336
+ for entry in package.solutions:
337
+ if '*' in str(entry.path):
338
+ for file in root.glob(str(entry.path)):
339
+ relative_file = file.relative_to(root)
340
+ add_solution(
341
+ Solution.model_copy(
342
+ entry, update={'path': relative_file}, deep=True
343
+ )
344
+ )
345
+ continue
346
+ add_solution(entry)
347
+ return res
296
348
 
297
349
 
298
350
  @functools.cache
@@ -214,8 +214,7 @@ def _upload_testcases(problem: api.Problem):
214
214
 
215
215
  def _upload_solutions(problem: api.Problem):
216
216
  console.console.print('Uploading main solution...')
217
- pkg = package.find_problem_package_or_die()
218
- main_solution = pkg.solutions[0]
217
+ main_solution = package.get_main_solution()
219
218
  if main_solution is None or main_solution.outcome != ExpectedOutcome.ACCEPTED:
220
219
  return
221
220
  problem.save_solution(
@@ -225,7 +224,7 @@ def _upload_solutions(problem: api.Problem):
225
224
  tag=api.SolutionTag.MA,
226
225
  )
227
226
 
228
- for i, solution in enumerate(pkg.solutions):
227
+ for i, solution in enumerate(package.get_solutions()):
229
228
  console.console.print(
230
229
  f'Uploading solution [item]{solution.path.name}[/item] (tag: [item]{_get_solution_tag(solution, is_first=i == 0)}[/item])...'
231
230
  )
rbx/box/schema.py CHANGED
@@ -9,7 +9,7 @@ from pydantic import AfterValidator, BaseModel, ConfigDict, Field, model_validat
9
9
  from pydantic_core import PydanticCustomError
10
10
 
11
11
  from rbx.autoenum import AutoEnum, alias
12
- from rbx.box.fields import FNameField, NameField
12
+ from rbx.box.fields import NameField
13
13
  from rbx.box.statements.expander import expand_statements
14
14
  from rbx.box.statements.schema import Statement
15
15
  from rbx.grading.steps import Outcome
@@ -257,7 +257,7 @@ class Testcase(BaseModel):
257
257
  class GeneratorCall(BaseModel):
258
258
  model_config = ConfigDict(extra='forbid')
259
259
 
260
- name: str = FNameField(description='The name of the generator to call.')
260
+ name: str = Field(description='The name of the generator to call.')
261
261
 
262
262
  args: Optional[str] = Field(
263
263
  default=None, description='The arguments to pass to the generator.'
@@ -355,7 +355,7 @@ problems that have points.
355
355
  class Generator(CodeItem):
356
356
  model_config = ConfigDict(extra='forbid')
357
357
 
358
- name: str = NameField(description="""The name of the generator.""")
358
+ name: str = Field(description="""The name of the generator.""")
359
359
 
360
360
 
361
361
  class Solution(CodeItem):
rbx/box/solutions.py CHANGED
@@ -158,12 +158,10 @@ def compile_solutions(
158
158
  tracked_solutions: Optional[Set[str]] = None,
159
159
  sanitized: bool = False,
160
160
  ) -> Dict[pathlib.Path, str]:
161
- pkg = package.find_problem_package_or_die()
162
-
163
161
  compiled_solutions = {}
164
162
 
165
163
  if tracked_solutions is None:
166
- tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
164
+ tracked_solutions = set(str(sol.path) for sol in package.get_solutions())
167
165
 
168
166
  for solution in expand_solutions(list(tracked_solutions)):
169
167
  if progress:
@@ -232,9 +230,8 @@ async def convert_list_of_solution_evaluations_to_dict(
232
230
  skeleton: SolutionReportSkeleton,
233
231
  items: Iterable[EvaluationItem],
234
232
  ) -> List[Dict[str, List[Evaluation]]]:
235
- pkg = package.find_problem_package_or_die()
236
233
  res: List[Dict[str, List[Evaluation]]] = [
237
- collections.defaultdict(list) for _ in pkg.solutions
234
+ collections.defaultdict(list) for _ in package.get_solutions()
238
235
  ]
239
236
 
240
237
  for item in items:
@@ -250,10 +247,9 @@ def _get_solutions_for_skeleton(
250
247
  tracked_solutions: Optional[Iterable[str]] = None,
251
248
  verification: VerificationLevel = VerificationLevel.NONE,
252
249
  ) -> List[Solution]:
253
- pkg = package.find_problem_package_or_die()
254
250
  solutions = [
255
251
  sol
256
- for sol in pkg.solutions
252
+ for sol in package.get_solutions()
257
253
  if verification.value >= VerificationLevel.ALL_SOLUTIONS.value or is_fast(sol)
258
254
  ]
259
255
  if tracked_solutions is not None:
@@ -739,8 +735,7 @@ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
739
735
 
740
736
 
741
737
  def expand_solutions_with_source(sols: List[str]) -> List[Tuple[Solution, bool]]:
742
- pkg = package.find_problem_package_or_die()
743
- pkg_sols = {str(sol.path): sol for sol in pkg.solutions}
738
+ pkg_sols = {str(sol.path): sol for sol in package.get_solutions()}
744
739
 
745
740
  # Download remote sols.
746
741
  path_sols = remote.expand_files(sols)
@@ -777,20 +772,21 @@ async def pick_solutions(
777
772
  tracked_solutions: Optional[OrderedSet[str]],
778
773
  extra_solutions: Optional[List[str]] = None,
779
774
  ) -> List[str]:
780
- pkg = package.find_problem_package_or_die()
781
775
  # Store in a separate list to maintain order with the package declaration.
782
776
  import questionary
783
777
 
778
+ solutions = package.get_solutions()
779
+
784
780
  choices = [
785
781
  questionary.Choice(
786
782
  title=_get_solution_repr(sol),
787
783
  value=str(sol.path),
788
784
  checked=tracked_solutions is None or str(sol.path) in tracked_solutions,
789
785
  )
790
- for sol in pkg.solutions
786
+ for sol in solutions
791
787
  ]
792
788
 
793
- seen_sols = set(str(sol.path) for sol in pkg.solutions)
789
+ seen_sols = set(str(sol.path) for sol in solutions)
794
790
 
795
791
  if extra_solutions is not None:
796
792
  # Add only new solutions.
@@ -339,7 +339,6 @@ async def build(
339
339
  vars: Annotated[
340
340
  Optional[List[str]],
341
341
  typer.Option(
342
- '-v',
343
342
  '--vars',
344
343
  help='Variables to be used in the statements.',
345
344
  ),
@@ -5,6 +5,8 @@ from typing import Optional
5
5
 
6
6
  import chardet
7
7
 
8
+ from rbx.utils import command_exists
9
+
8
10
  MAX_PDFLATEX_RUNS = 3
9
11
 
10
12
 
@@ -45,3 +47,12 @@ class Latex:
45
47
  return LatexResult(result=completed, pdf=None)
46
48
 
47
49
  return LatexResult(result=completed, pdf=output_path.read_bytes())
50
+
51
+
52
+ def install_tex_packages(path: pathlib.Path, cwd: pathlib.Path):
53
+ if not command_exists('texliveonfly'):
54
+ return
55
+ subprocess.run(
56
+ ['texliveonfly', path],
57
+ cwd=cwd,
58
+ )
rbx/grading/caching.py CHANGED
@@ -241,6 +241,7 @@ def _copy_hashed_files(artifact_list: List[GradingArtifacts], cacher: FileCacher
241
241
  ) is not None:
242
242
  # Use a symlink to the file in the persistent cache, if available.
243
243
  output.dest.unlink(missing_ok=True)
244
+ output.dest.parent.mkdir(parents=True, exist_ok=True)
244
245
  output.dest.symlink_to(path_to_symlink)
245
246
  else:
246
247
  # Otherwise, copy it.
@@ -469,6 +469,7 @@ class SandboxBase(abc.ABC):
469
469
  if override:
470
470
  real_path.unlink(missing_ok=True)
471
471
  try:
472
+ real_path.parent.mkdir(parents=True, exist_ok=True)
472
473
  real_path.symlink_to(utils.abspath(from_path))
473
474
  except NotImplementedError:
474
475
  return None
rbx/grading/steps.py CHANGED
@@ -355,6 +355,7 @@ def _process_output_artifacts(
355
355
  ):
356
356
  # File is in the persistent cache, store a symlink to it.
357
357
  dst.unlink(missing_ok=True)
358
+ dst.parent.mkdir(parents=True, exist_ok=True)
358
359
  dst.symlink_to(path_to_symlink)
359
360
  else:
360
361
  # File is not in the persistent cache, copy it.
@@ -1,6 +1,21 @@
1
1
  .box/
2
2
  build/
3
+ __pycache__/
3
4
 
5
+ .DS_Store
6
+ .vscode/
7
+
8
+ a.out
9
+ *.exe
10
+ *.pyc
4
11
  *.o
12
+ *~
13
+
14
+ *.fdb_latexmk
15
+ *.fls
16
+ *.log
17
+ *.synctex.gz
5
18
  *.aux
6
19
  *.log
20
+
21
+ *.un~
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  # yaml-language-server: $schema=https://rsalesc.github.io/rbx/schemas/Contest.json
3
- # Add problems by running `rbx contest add <problem-name> <short-name>`
3
+ # Add problems by running `rbx contest add`
4
4
  name: "new-contest"
5
5
  statements:
6
6
  - name: "statement-en"
7
7
  title: "New contest"
8
8
  language: "en"
9
9
  path: "statement/contest.rbx.tex"
10
- type: "jinja-tex"
10
+ type: JinjaTeX
11
11
  assets:
12
12
  - "statement/icpc.sty"
13
13
  - "statement/*.png"
@@ -1,6 +1,21 @@
1
1
  .box/
2
2
  build/
3
+ __pycache__/
3
4
 
5
+ .DS_Store
6
+ .vscode/
7
+
8
+ a.out
9
+ *.exe
10
+ *.pyc
4
11
  *.o
12
+ *~
13
+
14
+ *.fdb_latexmk
15
+ *.fls
16
+ *.log
17
+ *.synctex.gz
5
18
  *.aux
6
19
  *.log
20
+
21
+ *.un~
@@ -1,3 +1,4 @@
1
+ #include "rbx.h"
1
2
  #include "testlib.h"
2
3
 
3
4
  using namespace std;
@@ -6,7 +7,7 @@ int main(int argc, char *argv[]) {
6
7
  registerValidation(argc, argv);
7
8
  prepareOpts(argc, argv);
8
9
 
9
- int MAX_N = opt<int>("MAX_N"); // Read from package vars.
10
+ int MAX_N = getVar<int>("MAX_N"); // Read from package vars.
10
11
 
11
12
  inf.readInt(1, MAX_N, "A");
12
13
  inf.readSpace();
@@ -220,7 +220,7 @@
220
220
  \end{minipage}
221
221
  \end{tabular}
222
222
  \end{center}
223
- \vspace{-0.5cm}
223
+ \vspace{-0.1cm}
224
224
  }
225
225
  } % exampleInteractive
226
226
 
rbx/utils.py CHANGED
@@ -6,6 +6,7 @@ import os
6
6
  import os.path
7
7
  import pathlib
8
8
  import resource
9
+ import subprocess
9
10
  from typing import Any, Optional, Type, TypeVar
10
11
 
11
12
  import rich
@@ -145,6 +146,18 @@ def get_open_fds():
145
146
  return fds
146
147
 
147
148
 
149
+ def command_exists(command):
150
+ try:
151
+ subprocess.run(
152
+ [command], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
153
+ )
154
+ return True
155
+ except FileNotFoundError:
156
+ return False
157
+ except subprocess.CalledProcessError:
158
+ return True
159
+
160
+
148
161
  @contextlib.contextmanager
149
162
  def new_cd(x: pathlib.Path):
150
163
  d = os.getcwd()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.11.2
3
+ Version: 0.12.0
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -2,7 +2,7 @@ rbx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  rbx/annotations.py,sha256=qcJGL_INONSirH7LTrEma5RsweAIbO6QlRHVvRvb9ao,3521
3
3
  rbx/autoenum.py,sha256=cusv8ClXRlDVvhZ8eDrtYcL_2peXlHugAey_ht8roXk,12025
4
4
  rbx/box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- rbx/box/builder.py,sha256=MDm2qqmhedAbhn3rWP6cDwbBsGhV6sz_2sg1zLkPDw0,3613
5
+ rbx/box/builder.py,sha256=umrTdVAwvsOosBDVvDZ6kq1yWg3Z2Lxp2o1zK-V7BBk,3594
6
6
  rbx/box/cd.py,sha256=_XAzb3kV1NUaaRs8hc9SGDo10O1yh2_gr1EiAKzfUjI,2711
7
7
  rbx/box/checkers.py,sha256=wjS64fDrEkTjU6f80strt4QEBHPo4FQDh9ijELhOQT0,13016
8
8
  rbx/box/cli.py,sha256=a_tjWct-pin60cZ3OIg3SsyCjIoSWR0m6_lepFxDbDE,29778
@@ -10,12 +10,12 @@ rbx/box/code.py,sha256=4GChCeUaHjT7stvtPAURbCL1_V238geG3xmXQjbdV20,26708
10
10
  rbx/box/compile.py,sha256=Kzn5mEQu4vb91W9vjyt0DS6cfPJzFLTUoowFj7uHLUo,2539
11
11
  rbx/box/conftest.py,sha256=sEmciXSeDC-wmrZ1JSxbsUenKNP_VWW32mrCun2pY3I,1070
12
12
  rbx/box/contest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- rbx/box/contest/build_contest_statements.py,sha256=pNNYmQUNasFaAxfTLITnTeb_TNobjhtIBsxyG7QOWn4,12643
13
+ rbx/box/contest/build_contest_statements.py,sha256=OKf7BQ6DxtY65TpzjhNbY5Bu3wByrxZt7qCqWDXBaNQ,12693
14
14
  rbx/box/contest/contest_package.py,sha256=r6BGq3S4Ezj3Ep7pCmtvz3SN44npb2_rKJEoT5lSOqk,2985
15
15
  rbx/box/contest/contest_utils.py,sha256=fsWHG1e65wq9zvRY3tdf32VF0nU1yzGTOBX5yjXiNk4,1102
16
16
  rbx/box/contest/main.py,sha256=e_DjbrwpXTFZQaVWJO7prJyehJ702GkVEAGEraRK6h4,8352
17
17
  rbx/box/contest/schema.py,sha256=eb7xtyq078YYWYHueximNhyHFINzwgLMFm1j9U3LxBQ,7461
18
- rbx/box/contest/statements.py,sha256=g_h4upUrfG9pIuNa2vBYmpLk6H1BhcCpyKRv7zav60M,3974
18
+ rbx/box/contest/statements.py,sha256=dI3AmaTYfOzyB1uh75ST9se9o_QqXzar4pNkIdC-_G8,3956
19
19
  rbx/box/creation.py,sha256=oTAC11XV2Pw2YAlF_d11Eo7A1fD6ItlpFMpLEzMLyFI,1331
20
20
  rbx/box/deferred.py,sha256=II3X9e87JCOZtmspnHh-n4PFqh-FsH_oc0XJHZ9ZYVQ,691
21
21
  rbx/box/download.py,sha256=tLW5gLVeLk0gHMEMwScSoHIXQPkXuPsqXzItsrsnUZY,3070
@@ -24,7 +24,7 @@ rbx/box/environment.py,sha256=iR-VTNvbW8iNienWKYVnd1xxCuhWml7bYa5FTIZCOY0,13574
24
24
  rbx/box/extensions.py,sha256=Von8kIeXvNFTkGlMRMTvL2HIHPwlkuiMswr-ydbGV1w,519
25
25
  rbx/box/fields.py,sha256=lc1OHpo_AC8RxzNasipULGkRmToAiXBGzWDeb14L_ss,1092
26
26
  rbx/box/formatting.py,sha256=i3vXHpo_L_VpVPxOe4wHlai1WhlDJlfxUexS9DC0Szg,1249
27
- rbx/box/generators.py,sha256=0HiNZfZXvEpXLHP1D0cJp-fgSujOm4jPDdniWyy8HrI,13824
27
+ rbx/box/generators.py,sha256=yu1CAW-u3M1EhfSZldhIcPTwIFBGC8OzWxVGR-kufTk,15366
28
28
  rbx/box/generators_test.py,sha256=J7aBfuJhU84MWDWzgReRoOuQw_hVa09B8gTKAvL2XVo,1987
29
29
  rbx/box/git_utils.py,sha256=VlUgzuHOCnrjjiJQnDB32qDHbHw_zkwgA7wm4bloibc,750
30
30
  rbx/box/global_package.py,sha256=OMnvqY8VQlP9YVSZwH5LCVkTsE7zNIhhRcslIWktkQc,2016
@@ -35,7 +35,7 @@ rbx/box/lazy_importing_test.py,sha256=B0-b3y_DkxEmtVfu4NfmVsgVdFl6kRCsEL6GLMHJIS
35
35
  rbx/box/linting.py,sha256=wRE0hKCduTBHZYBFmmis_d9AMTsDu0Q-AjByCeTnkrY,3187
36
36
  rbx/box/main.py,sha256=a8CYi77kOywPFly4-ucEIJLXQW-1NFp91kK2fA42YTE,86
37
37
  rbx/box/naming.py,sha256=pOG37X_wQM9CCSYwJIUf-b-ZHEs_nchO7wQEdP_quJg,1367
38
- rbx/box/package.py,sha256=ezSiqCl0EYEiasMeg8XF79Kyh6kqvabmrobNveEzcLM,13554
38
+ rbx/box/package.py,sha256=VyG7TZKYGeqzilPkLs8rbJDa1kTIXfrZADequnJvyE4,15110
39
39
  rbx/box/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  rbx/box/packaging/boca/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
41
  rbx/box/packaging/boca/extension.py,sha256=EQALNEOv4zVDXSKs_dk11n92y7cBZVn8TogIK683lE0,890
@@ -48,7 +48,7 @@ rbx/box/packaging/pkg/packager.py,sha256=4pt4cZa_MU9gsWNVvh5Um1KEIh_axBHlXmPf6rp
48
48
  rbx/box/packaging/polygon/packager.py,sha256=K8WKYO-HBDx_z4R43Ko7YBs1bbFjhFPx8IUkvY0jM0I,11190
49
49
  rbx/box/packaging/polygon/polygon_api.py,sha256=mPKEqiwANJ1nr-JhOgzGMaDhnbljsAgzzPHW6kkf7R4,41016
50
50
  rbx/box/packaging/polygon/test.py,sha256=bgEju5PwudgyfwxXJagm8fM6CJVlWM6l_-2q1V-oKaQ,3069
51
- rbx/box/packaging/polygon/upload.py,sha256=6dIbjwygKtirLBkbh40UgtHU1NHGxSMoFAtg2BcC2II,12902
51
+ rbx/box/packaging/polygon/upload.py,sha256=tymodSg5Bv1pnG5q6b_LYvRBs-m3PcyXL4TS0UmZwEk,12875
52
52
  rbx/box/packaging/polygon/xml_schema.py,sha256=ZgcLyvxggMUccbTNdzflue5G-FTN2_ZmOGGF7FD0Y5A,2851
53
53
  rbx/box/presets/__init__.py,sha256=L6fy_hetXnB8Eo_MO3oadv_ce2hwVW2J-BJm9CooEdA,33432
54
54
  rbx/box/presets/fetch.py,sha256=900aq9S8e12TlgSenG0iHgtF4OWgqavZsptgI_a1YKM,2508
@@ -57,17 +57,17 @@ rbx/box/presets/schema.py,sha256=iOGWHqJ9Z-ljQV8ljOjQMuCwqJQLJKm_pYX9cLjZbmE,243
57
57
  rbx/box/remote.py,sha256=PsJ4i3suQEr3cxiKk4nCho98QBGs5b2v8_TEvn_nE-o,5204
58
58
  rbx/box/retries.py,sha256=BZsi4sYBjm3VK5zb_pBQSYQuKo3ZntmtEFoVPZHg4QI,4982
59
59
  rbx/box/sanitizers/warning_stack.py,sha256=6-rr3dkMq6MpfjrVZ8lSQjF4RZ5YzZSAPMzHCfm-6h4,2876
60
- rbx/box/schema.py,sha256=424VcpZzY2qUz8rlETwIqrzzwmnoARILEO7Wf2P61BY,18855
60
+ rbx/box/schema.py,sha256=kOWMe0lrAJBEhU5gBAWvhwMHHzWx17PBwThou4zuo1A,18834
61
61
  rbx/box/setter_config.py,sha256=WHlWjjdHNoBbCdZqYokctTSxvtPqQXGmrgDHyaK4FKk,4985
62
- rbx/box/solutions.py,sha256=I8DqQ4HDO_MWlabqJHnCgG7udDgUgox15YW5TCrN-8o,53864
62
+ rbx/box/solutions.py,sha256=CmwLS7EJsZ6_4HqU0bKV8oy2Vd6wKwdQS3R9AH3XNZQ,53696
63
63
  rbx/box/solutions_test.py,sha256=PX1TQoRzNd9mi1SGsG7WFrpqFgNrNX5Kwt0mkwFdoOA,1749
64
64
  rbx/box/state.py,sha256=MMf3DvfQji0jKEliCHct2Tpp_0epL1tvP8HbHNArQIc,166
65
65
  rbx/box/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
- rbx/box/statements/build_statements.py,sha256=DgEuhhbu-LJAMFJhmVuBR44o4Mq2odue9pRaG-I5OJY,12711
66
+ rbx/box/statements/build_statements.py,sha256=-pUQAS8CEnWqHtpKW6wH338rQg3FKmb9qYxilzcwJFA,12693
67
67
  rbx/box/statements/builders.py,sha256=Qb_rfkFOqghFZfEf2zPSEoDPc-lCBNb5CcPoOkV7HMk,13747
68
68
  rbx/box/statements/expander.py,sha256=sdbMtNcJQCbXGIkFIl9h24pGr77vhFLnM31V5AfuduI,1715
69
69
  rbx/box/statements/joiners.py,sha256=jItNXkAbTjFQpPMgfDMW86n3vMTbaE8sgo9I8Yf4Txg,2886
70
- rbx/box/statements/latex.py,sha256=LkcHwXjMFxbw--Gj9T1VkFKQFsXhY9dN7xZHpZycNW8,1346
70
+ rbx/box/statements/latex.py,sha256=ipTGjL4kjAsnqgiH6Pk1PwKFegBumQP4-y0pFAbNN8I,1584
71
71
  rbx/box/statements/latex_jinja.py,sha256=iMx47ynKMjLNcfymzHV24jtWrRVnit0Va9H8yTfgmiA,10379
72
72
  rbx/box/statements/schema.py,sha256=5_qrY1KztCLSe4t-rJ7zdv3cBjcaO-FnFc45ZRUotfs,5127
73
73
  rbx/box/stats.py,sha256=rUAnmp7kTgUvIQ56NLpQaIQkazB37MVcUos5en3xUQw,3258
@@ -121,14 +121,14 @@ rbx/console.py,sha256=X8EJy68OROgh6ao3ZcUjZm5Y56VFMzen58ywAuQ7pAU,990
121
121
  rbx/create.py,sha256=ezUq9KiSA-88ASd8CtjWXw8UB4LCaQ3Gib3OgvsLK-Q,986
122
122
  rbx/edit.py,sha256=Zqnx_Pt06ijCxV-pZKGCJhjKB-nVO0QCM6xSBwPWGoE,798
123
123
  rbx/grading/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
- rbx/grading/caching.py,sha256=X20v-ZCmcT6OXvWzq2VBdACi6_cEYWB2phkiJ34CNeg,15306
124
+ rbx/grading/caching.py,sha256=Inu9IYtjO6reg7XGCP44AcaJ5Ax1McfkfNwcpjTCzrw,15376
125
125
  rbx/grading/conftest.py,sha256=820Uw3AE-dwAfwLhXjgq_PixpDI1-JEXsOPYf4VT5H8,971
126
126
  rbx/grading/debug_context.py,sha256=kuAXEI8yRG8xfhS9WKKIRh9X0e5JUD8zvl_cpczJTC8,699
127
127
  rbx/grading/grading_context.py,sha256=TaRyLwPkkxvspQIFUFk8Ok0T8EST2pHMMNoVDx9lbFU,3416
128
128
  rbx/grading/judge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
129
  rbx/grading/judge/cacher.py,sha256=wtRnVE_Es9xcqIgrhn6WzUoB90aihUuKaFyuVsusngg,20323
130
130
  rbx/grading/judge/digester.py,sha256=gtOIe_iL4PEWA7OKelW1gjSI-nBvbOpDPJGV8VQyjSg,912
131
- rbx/grading/judge/sandbox.py,sha256=bk-DBcgjbYoU69IFt-YbIy2-GnCupxv0t6YXQQ_6ZwU,26276
131
+ rbx/grading/judge/sandbox.py,sha256=m0Or5UWnKyPuaJNfb-OiVucHT04ZPIb9_QJrkie1Pok,26340
132
132
  rbx/grading/judge/sandboxes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  rbx/grading/judge/sandboxes/isolate.py,sha256=SbzmvjnbMRIHWV68_6jdZ7dZRnC-Hp9ACNyv8OS_Npg,25991
134
134
  rbx/grading/judge/sandboxes/stupid_sandbox.py,sha256=_5p3IFmrDhyky8RCyc6fjUCiBf2SFSJ99O_4sj7xzTE,11342
@@ -139,7 +139,7 @@ rbx/grading/judge/testiso.py,sha256=v14DtkWiZFJ9AKMzrb0_vZKPWDt8jz8iIw1Z2O-Advk,
139
139
  rbx/grading/limits.py,sha256=ev312UTOo8S4-3AAVibQdXZclWCxS96CdbZxqW4y1kE,770
140
140
  rbx/grading/processing_context.py,sha256=Jg9kNnkH3hi2hiE6Gh23QwS89r9Zj230NMl1CUEHSfo,1866
141
141
  rbx/grading/profiling.py,sha256=OEdtoAzjYjLfi-QI5Ke7tLZzJeqvGpMB2utQBNuH3E4,3369
142
- rbx/grading/steps.py,sha256=ChDsSMkSwMjT1NiLe3sBhXuzwuxeLWJjvne9BnwBpDQ,30127
142
+ rbx/grading/steps.py,sha256=JlR72UvEdvJGFZGA0Jd_2qUoEnElhiIOQAzbUrOhidk,30185
143
143
  rbx/grading/steps_with_caching.py,sha256=b6NVZN46IG2r49uQ6u19Jj6AJ_nQFO6_pcKq4dWTwuQ,4579
144
144
  rbx/grading/steps_with_caching_run_test.py,sha256=Ldrs1VS01TWYpqg1x9LeiF7_TejsE_suL3y1MI5pAlU,26599
145
145
  rbx/grading_utils.py,sha256=lL2KtSkOsMElqrRoApQTbFcqVOeHVWUDTMCa3IsLpC4,4484
@@ -202,14 +202,14 @@ rbx/resources/packagers/moj/scripts/py2/run.sh,sha256=qshf-K3mKP4c7b45S3D82Wj0Ai
202
202
  rbx/resources/packagers/moj/scripts/py3/compile.sh,sha256=XPn8qDR_gPAAZD9h5lVEMdBkrSogRZvpT4MAaNNp9nk,96
203
203
  rbx/resources/packagers/moj/scripts/py3/prep.sh,sha256=it1e07QRpsnku3-rXOO1ovaw-RJlVVPy9R3I6WWwgMM,126
204
204
  rbx/resources/packagers/moj/scripts/py3/run.sh,sha256=LrMi7Tap9no8gh64QNGUXbWauP6ZpSl-wEwXZ2qhPo0,197
205
- rbx/resources/presets/default/contest/.gitignore,sha256=PQMEIEPz0BCdUko1UKhRhcMNhFKA9129LzbbflAGQ6w,30
206
- rbx/resources/presets/default/contest/contest.rbx.yml,sha256=Q91BwE6F0gS7fi7qWelXis9dZ0rzAQbwA1l9gbFauY8,876
205
+ rbx/resources/presets/default/contest/.gitignore,sha256=CMwGD717vKcbQrXjha2D4LMwjDfQcev8rjFPg0AIi4A,131
206
+ rbx/resources/presets/default/contest/contest.rbx.yml,sha256=7ifI6vbQg9IOYA94Q6i1toj-S8wNwwDkK-gJLxEaCeU,845
207
207
  rbx/resources/presets/default/contest/statement/contest.rbx.tex,sha256=Jx6op_WdVpQOMekvOAZnBzDxxvBzg1_9ZFWtbzGasLo,793
208
208
  rbx/resources/presets/default/contest/statement/instructions.tex,sha256=JG_eR13ukZgEahrrmrbg40H8cUzpoUE8QLocihN-fZ8,2414
209
209
  rbx/resources/presets/default/contest/statement/logo.png,sha256=RLNYmZoc-BR6AZKkmr4UEg3h01YeFzvy604jMAQC7aA,414485
210
210
  rbx/resources/presets/default/env.rbx.yml,sha256=quSPG5Xs9KroYLATNLPNtORLGRWtrLLt2Fx81T1enAM,1692
211
211
  rbx/resources/presets/default/preset.rbx.yml,sha256=s7OFotxoe3aFFEstJv5QG7LU3UP370dGe5hA9gWRQpg,498
212
- rbx/resources/presets/default/problem/.gitignore,sha256=PQMEIEPz0BCdUko1UKhRhcMNhFKA9129LzbbflAGQ6w,30
212
+ rbx/resources/presets/default/problem/.gitignore,sha256=CMwGD717vKcbQrXjha2D4LMwjDfQcev8rjFPg0AIi4A,131
213
213
  rbx/resources/presets/default/problem/gens/gen.cpp,sha256=rn6sGRjZ1sFE1Rq02r6488iquY9xTrutcvLv4d1sohA,178
214
214
  rbx/resources/presets/default/problem/manual_tests/samples/000.in,sha256=w66OEtCJGqjUNj8cJrqgImgGVm8W_OlIUtF255ds-ow,4
215
215
  rbx/resources/presets/default/problem/manual_tests/samples/001.in,sha256=P4QInDX87xXoDWu4PVIzUeNW5LtTlUKbMCvJ9uZOPGw,20
@@ -221,10 +221,10 @@ rbx/resources/presets/default/problem/sols/wa.cpp,sha256=Bj7tejPIlXG_JqUHWY1zi9T
221
221
  rbx/resources/presets/default/problem/statement/statement.rbx.tex,sha256=JHdiMN3NQQsysDA1w3RfOGmDFobCc68YCB6SURy2hHo,360
222
222
  rbx/resources/presets/default/problem/testplan/random.py,sha256=-iPorU24QHfp39EYRJX9jMKcTIxxz5ejKoAzPLIuu1g,98
223
223
  rbx/resources/presets/default/problem/testplan/random.txt,sha256=2BA_AM8IAKEcrUTJhnzWnNJN8whDN82E2137NhFkt2U,137
224
- rbx/resources/presets/default/problem/validator.cpp,sha256=w4gl1u30Dx1N9oRTVtIg_rPlIyL6QdN_Bi5FDpp4gyw,317
224
+ rbx/resources/presets/default/problem/validator.cpp,sha256=I_Vs12xQnJnwkRtCu4EjazdaERms4GktZhME7zGaQjU,337
225
225
  rbx/resources/presets/default/problem/wcmp.cpp,sha256=gbjJe3Vf9-YzHCEqBUq30aI3jMZXhqBDn3jjecYOn-w,902
226
226
  rbx/resources/presets/default/shared/contest_template.rbx.tex,sha256=-sExA0H7vo08I6P0dT78_xNN5Q9wiu6KZzB75s12Xm4,1293
227
- rbx/resources/presets/default/shared/icpc.sty,sha256=1OpcnKnNXtwubvvco2aGwhj-Xe6VGm7w0isWEUrbQT8,8042
227
+ rbx/resources/presets/default/shared/icpc.sty,sha256=04feEwL7LRRvUFopwVAVjxdTvzE5gQtFsSkm6iJ5hLo,8042
228
228
  rbx/resources/presets/default/shared/problem_template.rbx.tex,sha256=AjziXNWluAGsQiPQ2r477RkrMpNQJ1kcgidU8T_C4KA,1281
229
229
  rbx/resources/templates/rbx.h,sha256=Iwtmr2gdDYmZ2VlIurmleBb_uEpriWd4EX0dJta8xUA,2179
230
230
  rbx/resources/templates/template.cpp,sha256=xXWpWo7fa7HfmPNqkmHcmv3i46Wm0ZL-gPmkRfGvLn4,317
@@ -238,9 +238,9 @@ rbx/test.py,sha256=lrjIWESMedaDLX_knikPsJT322oI2nvy8weCslEuHLU,11810
238
238
  rbx/testcase.py,sha256=yKOq3CAJZ1YTmInvnoIs0u1iJnRj_X85XiWbLI-p9d8,1951
239
239
  rbx/testcase_rendering.py,sha256=nfmv6dSEqd4aR3TsaODwkKGK6AXty_DDKtWf_ejiQpI,2084
240
240
  rbx/testing_utils.py,sha256=x_PqD8Zd2PkN91NxVHUnSTs044-1WK5KKtttKQBXpFs,2083
241
- rbx/utils.py,sha256=YvN0q1vaLR9HxUfXKOTx1iN6Bp_fI8YNTQiJ8TNq7eM,4957
242
- rbx_cp-0.11.2.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
243
- rbx_cp-0.11.2.dist-info/METADATA,sha256=B8oCqJ2AKoeL8kM9BxGo88pM3Y_ujJtzPCPDhsC5j7U,4625
244
- rbx_cp-0.11.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
245
- rbx_cp-0.11.2.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
246
- rbx_cp-0.11.2.dist-info/RECORD,,
241
+ rbx/utils.py,sha256=SZW_bqU33CkHRrInnGN36QfQA0dQx2zdWO32fxlDy-w,5264
242
+ rbx_cp-0.12.0.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
243
+ rbx_cp-0.12.0.dist-info/METADATA,sha256=moF508pqK5J5V43KW2LiDjuxZkjzL0DlHoa_OZvAjpc,4625
244
+ rbx_cp-0.12.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
245
+ rbx_cp-0.12.0.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
246
+ rbx_cp-0.12.0.dist-info/RECORD,,