rbx.cp 0.5.16__py3-none-any.whl → 0.5.17__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
@@ -76,7 +76,7 @@ def build(
76
76
  return True
77
77
 
78
78
 
79
- def verify(verification: environment.VerificationParam) -> bool:
79
+ async def verify(verification: environment.VerificationParam) -> bool:
80
80
  if not build(verification=verification):
81
81
  return False
82
82
 
@@ -100,7 +100,7 @@ def verify(verification: environment.VerificationParam) -> bool:
100
100
 
101
101
  console.console.print()
102
102
  console.console.rule('[status]Run report[/status]', style='status')
103
- return print_run_report(
103
+ return await print_run_report(
104
104
  solution_result,
105
105
  console.console,
106
106
  verification,
rbx/box/cd.py CHANGED
@@ -1,10 +1,13 @@
1
+ import contextlib
1
2
  import functools
2
3
  import pathlib
3
4
  from typing import Optional
4
5
 
5
6
  import typer
6
7
 
7
- from rbx import console, utils
8
+ from rbx import console
9
+ from rbx.box.sanitizers import warning_stack
10
+ from rbx.utils import new_cd
8
11
 
9
12
 
10
13
  def find_package(root: pathlib.Path = pathlib.Path()) -> Optional[pathlib.Path]:
@@ -30,7 +33,14 @@ def within_closest_package(func):
30
33
  console.console.print('[error]No rbx package found.[/error]')
31
34
  raise typer.Exit(1)
32
35
  # Get deepest package.
33
- with utils.new_cd(package):
36
+ with new_package_cd(package):
34
37
  return func(*args, **kwargs)
35
38
 
36
39
  return wrapper
40
+
41
+
42
+ @contextlib.contextmanager
43
+ def new_package_cd(x: pathlib.Path):
44
+ with new_cd(x):
45
+ yield
46
+ warning_stack.print_warning_stack_report()
rbx/box/checkers.py CHANGED
@@ -5,7 +5,7 @@ import typer
5
5
 
6
6
  from rbx import console
7
7
  from rbx.box import package
8
- from rbx.box.code import compile_item, run_item
8
+ from rbx.box.code import SanitizationLevel, compile_item, run_item
9
9
  from rbx.box.schema import Testcase
10
10
  from rbx.grading.judge.sandbox import SandboxBase
11
11
  from rbx.grading.steps import (
@@ -23,7 +23,7 @@ def compile_checker() -> str:
23
23
  checker = package.get_checker()
24
24
 
25
25
  try:
26
- digest = compile_item(checker)
26
+ digest = compile_item(checker, sanitized=SanitizationLevel.PREFER)
27
27
  except Exception as e:
28
28
  console.console.print('[error]Failed compiling checker.[/error]')
29
29
  raise typer.Exit(1) from e
@@ -33,11 +33,21 @@ def compile_checker() -> str:
33
33
  def _check_pre_output(run_log: Optional[RunLog]) -> CheckerResult:
34
34
  pkg = package.find_problem_package_or_die()
35
35
 
36
+ is_sanitized = (
37
+ run_log is not None
38
+ and run_log.metadata is not None
39
+ and run_log.metadata.is_sanitized
40
+ )
41
+
36
42
  if run_log is None:
37
43
  return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
38
44
 
39
45
  timelimit = pkg.timelimit_for_language(run_log.get_run_language())
40
- if run_log.time is not None and run_log.time * 1000 > timelimit * 2:
46
+ if (
47
+ run_log.time is not None
48
+ and run_log.time * 1000 > timelimit * 2
49
+ and not is_sanitized
50
+ ):
41
51
  return CheckerResult(outcome=Outcome.TIME_LIMIT_EXCEEDED)
42
52
 
43
53
  if run_log.exitstatus in [SandboxBase.EXIT_SIGNAL, SandboxBase.EXIT_NONZERO_RETURN]:
@@ -58,11 +68,17 @@ def _convert_tle(result: CheckerResult, run_log: Optional[RunLog]) -> CheckerRes
58
68
  # This already is a TLE outcome.
59
69
  return result
60
70
  pkg = package.find_problem_package_or_die()
71
+ is_sanitized = (
72
+ run_log is not None
73
+ and run_log.metadata is not None
74
+ and run_log.metadata.is_sanitized
75
+ )
61
76
  if (
62
77
  run_log is not None
63
78
  and run_log.time is not None
64
79
  and run_log.time * 1000
65
80
  >= pkg.timelimit_for_language(run_log.get_run_language())
81
+ and not is_sanitized
66
82
  ):
67
83
  # Soft TLE.
68
84
  result.no_tle_outcome = result.outcome
@@ -75,7 +91,7 @@ def check_with_no_output(run_log: Optional[RunLog]) -> CheckerResult:
75
91
  return _convert_tle(result, run_log)
76
92
 
77
93
 
78
- def check(
94
+ def _check(
79
95
  checker_digest: str,
80
96
  run_log: Optional[RunLog],
81
97
  testcase: Testcase,
@@ -157,3 +173,21 @@ def check(
157
173
  if skip_run_log:
158
174
  return result
159
175
  return _convert_tle(result, run_log)
176
+
177
+
178
+ def _check_sanitizer_warnings(run_log: Optional[RunLog]) -> bool:
179
+ if run_log is None:
180
+ return False
181
+ return run_log.warnings
182
+
183
+
184
+ def check(
185
+ checker_digest: str,
186
+ run_log: Optional[RunLog],
187
+ testcase: Testcase,
188
+ program_output: pathlib.Path,
189
+ skip_run_log: bool = False,
190
+ ) -> CheckerResult:
191
+ result = _check(checker_digest, run_log, testcase, program_output, skip_run_log)
192
+ result.sanitizer_warnings = _check_sanitizer_warnings(run_log)
193
+ return result
rbx/box/code.py CHANGED
@@ -1,17 +1,19 @@
1
1
  import pathlib
2
2
  import shlex
3
- import sys
3
+ from enum import Enum
4
4
  from pathlib import PosixPath
5
5
  from typing import List, Optional
6
6
 
7
+ import rich
8
+ import rich.text
7
9
  import typer
8
10
 
9
- from rbx.box import download, package
11
+ from rbx import console
12
+ from rbx.box import download, package, setter_config
10
13
  from rbx.box.environment import (
11
14
  ExecutionConfig,
12
15
  get_compilation_config,
13
16
  get_execution_config,
14
- get_extension_or_default,
15
17
  get_file_mapping,
16
18
  get_language,
17
19
  get_mapped_command,
@@ -19,7 +21,7 @@ from rbx.box.environment import (
19
21
  get_sandbox_params_from_config,
20
22
  merge_execution_configs,
21
23
  )
22
- from rbx.box.extensions import MacExtension
24
+ from rbx.box.sanitizers import warning_stack
23
25
  from rbx.box.schema import CodeItem
24
26
  from rbx.grading import steps_with_caching
25
27
  from rbx.grading.steps import (
@@ -31,17 +33,25 @@ from rbx.grading.steps import (
31
33
  GradingFileOutput,
32
34
  RunLog,
33
35
  RunLogMetadata,
36
+ is_cxx_command,
34
37
  )
35
38
 
36
39
 
37
- def normalize_for_macos(commands: List[str]) -> List[str]:
38
- def normalize(command: str) -> str:
39
- extension = get_extension_or_default('mac', MacExtension)
40
- if extension.gpp_alternative is None:
41
- return command
42
- return command.replace('g++', extension.gpp_alternative)
40
+ class SanitizationLevel(Enum):
41
+ NONE = 0
42
+ PREFER = 1
43
+ FORCE = 2
43
44
 
44
- return [normalize(command) for command in commands]
45
+ def should_sanitize(self) -> bool:
46
+ cfg = setter_config.get_setter_config()
47
+ if cfg.sanitizers.enabled:
48
+ return self.value >= SanitizationLevel.PREFER.value
49
+ return self.value >= SanitizationLevel.FORCE.value
50
+
51
+
52
+ def substitute_commands(commands: List[str], sanitized: bool = False) -> List[str]:
53
+ cfg = setter_config.get_setter_config()
54
+ return [cfg.substitute_command(command, sanitized) for command in commands]
45
55
 
46
56
 
47
57
  def get_extension(code: CodeItem) -> str:
@@ -55,8 +65,46 @@ def find_language_name(code: CodeItem) -> str:
55
65
  return get_language(get_extension(code)).name
56
66
 
57
67
 
68
+ def is_executable_sanitized(executable: DigestOrSource) -> bool:
69
+ if executable.digest is None:
70
+ return False
71
+ storage = package.get_cache_storage()
72
+ return storage.exists(f'{executable.digest.value}.san')
73
+
74
+
75
+ def add_sanitizer_flags_to_command(command: str) -> str:
76
+ if is_cxx_command(command):
77
+ return command + ' -fsanitize=address,undefined -fno-omit-frame-pointer'
78
+ return command
79
+
80
+
81
+ def add_sanitizer_flags(commands: List[str]) -> List[str]:
82
+ return [add_sanitizer_flags_to_command(command) for command in commands]
83
+
84
+
85
+ def add_warning_flags_to_command(command: str) -> str:
86
+ if is_cxx_command(command):
87
+ return (
88
+ command
89
+ + ' -Wall -Wshadow -Wno-unused-result -Wno-sign-compare -Wno-char-subscripts'
90
+ )
91
+ return command
92
+
93
+
94
+ def add_warning_flags(commands: List[str], force_warnings: bool) -> List[str]:
95
+ cfg = setter_config.get_setter_config()
96
+ if cfg.warnings.enabled or force_warnings:
97
+ return [add_warning_flags_to_command(command) for command in commands]
98
+ return commands
99
+
100
+
58
101
  # Compile code item and return its digest in the storage.
59
- def compile_item(code: CodeItem) -> str:
102
+ def compile_item(
103
+ code: CodeItem,
104
+ sanitized: SanitizationLevel = SanitizationLevel.PREFER,
105
+ force_warnings: bool = False,
106
+ verbose: bool = False,
107
+ ) -> str:
60
108
  generator_path = PosixPath(code.path)
61
109
  language = find_language_name(code)
62
110
  compilation_options = get_compilation_config(language)
@@ -69,10 +117,12 @@ def compile_item(code: CodeItem) -> str:
69
117
  # Language is not compiled.
70
118
  return sandbox.file_cacher.put_file_from_path(generator_path)
71
119
 
72
- # Compile the generator
73
120
  commands = get_mapped_commands(compilation_options.commands, file_mapping)
74
- if sys.platform == 'darwin':
75
- commands = normalize_for_macos(commands)
121
+ commands = add_warning_flags(commands, force_warnings)
122
+ commands = substitute_commands(commands, sanitized=sanitized.should_sanitize())
123
+
124
+ if sanitized.should_sanitize():
125
+ commands = add_sanitizer_flags(commands)
76
126
 
77
127
  compiled_digest = DigestHolder()
78
128
 
@@ -104,6 +154,33 @@ def compile_item(code: CodeItem) -> str:
104
154
  raise typer.Exit(1)
105
155
 
106
156
  assert compiled_digest.value is not None
157
+
158
+ if verbose and artifacts.logs is not None and artifacts.logs.preprocess is not None:
159
+ for log in artifacts.logs.preprocess:
160
+ console.console.print(f'[status]Command:[/status] {log.get_command()}')
161
+ console.console.print(f'[status]Summary:[/status] {log.get_summary()}')
162
+ console.console.print(rich.text.Text.from_ansi(log.log), style='default')
163
+
164
+ # Write compiler warnings.
165
+ cfg = setter_config.get_setter_config()
166
+ if (
167
+ (cfg.warnings.enabled or force_warnings)
168
+ and artifacts.logs is not None
169
+ and artifacts.logs.preprocess is not None
170
+ ):
171
+ any_warning = any(log.warnings for log in artifacts.logs.preprocess)
172
+ if any_warning:
173
+ warning_stack.get_warning_stack().add_warning(code)
174
+
175
+ # Create sentinel to indicate this executable is sanitized.
176
+ storage = package.get_cache_storage()
177
+ if sanitized.should_sanitize():
178
+ pf = storage.create_file(f'{compiled_digest.value}.san')
179
+ if pf is not None:
180
+ storage.commit_file(pf)
181
+ elif storage.exists(f'{compiled_digest.value}.san'):
182
+ storage.delete(f'{compiled_digest.value}.san')
183
+
107
184
  return compiled_digest.value
108
185
 
109
186
 
@@ -127,14 +204,35 @@ def run_item(
127
204
  sandbox = package.get_singleton_sandbox()
128
205
  sandbox_params = get_sandbox_params_from_config(execution_options.sandbox)
129
206
 
207
+ # Sanitization parameters.
208
+ sanitized = False
209
+ if is_executable_sanitized(executable):
210
+ # Remove any memory constraints for a sanitized executable.
211
+ # Sanitizers are known to be memory-hungry.
212
+ sandbox_params.address_space = None
213
+
214
+ # Reset timeout configs since sanitizers are known to be time-hungry.
215
+ sandbox_params.timeout = None
216
+ sandbox_params.wallclock_timeout = None
217
+ orig_execution_config = get_execution_config(language)
218
+ if orig_execution_config.sandbox is not None:
219
+ sandbox_params.timeout = orig_execution_config.sandbox.timeLimit
220
+ sandbox_params.wallclock_timeout = (
221
+ orig_execution_config.sandbox.wallTimeLimit
222
+ )
223
+ sanitized = True
224
+
130
225
  sandbox_params.set_stdall(
131
226
  stdin=PosixPath(file_mapping.input) if stdin is not None else None,
132
227
  stdout=PosixPath(file_mapping.output) if stdout is not None else None,
133
- stderr=PosixPath(file_mapping.error) if stderr is not None else None,
228
+ stderr=PosixPath(file_mapping.error)
229
+ if stderr is not None or sanitized
230
+ else None,
134
231
  )
135
232
 
136
233
  assert execution_options.command
137
234
  command = get_mapped_command(execution_options.command, file_mapping)
235
+ command = substitute_commands([command], sanitized=sanitized)[0]
138
236
 
139
237
  if extra_args is not None:
140
238
  splitted_command = shlex.split(command)
@@ -175,11 +273,21 @@ def run_item(
175
273
  if outputs:
176
274
  artifacts.outputs.extend(outputs)
177
275
 
178
- return steps_with_caching.run(
276
+ run_log = steps_with_caching.run(
179
277
  command,
180
278
  params=sandbox_params,
181
279
  sandbox=sandbox,
182
280
  artifacts=artifacts,
183
281
  dependency_cache=dependency_cache,
184
- metadata=RunLogMetadata(language=code.language),
282
+ metadata=RunLogMetadata(language=code.language, is_sanitized=sanitized),
185
283
  )
284
+
285
+ # Find sanitizer logs.
286
+ if run_log is not None and run_log.warnings:
287
+ assert sandbox_params.stderr_file is not None
288
+ stderr_output = artifacts.get_output_file_for_src(sandbox_params.stderr_file)
289
+ if stderr_output is not None:
290
+ warning_stack.get_warning_stack().add_sanitizer_warning(
291
+ package.get_cache_storage(), code, stderr_output
292
+ )
293
+ return run_log
rbx/box/compile.py CHANGED
@@ -4,6 +4,8 @@ import typer
4
4
 
5
5
  from rbx import annotations, console
6
6
  from rbx.box import code, package
7
+ from rbx.box.code import SanitizationLevel
8
+ from rbx.box.sanitizers import warning_stack
7
9
  from rbx.box.schema import CodeItem
8
10
 
9
11
  app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
@@ -13,38 +15,68 @@ def _compile_out():
13
15
  return package.get_build_path() / 'exe'
14
16
 
15
17
 
16
- def _compile(item: CodeItem):
18
+ def _compile(item: CodeItem, sanitized: SanitizationLevel, warnings: bool):
17
19
  console.console.print(f'Compiling [item]{item.path}[/item]...')
18
- digest = code.compile_item(item)
20
+ digest = code.compile_item(item, sanitized, force_warnings=warnings, verbose=True)
19
21
  cacher = package.get_file_cacher()
20
22
  out_path = _compile_out()
21
23
  cacher.get_file_to_path(digest, out_path)
22
24
  out_path.chmod(0o755)
23
25
 
26
+ # Clear the warning stack as we don't really worry about it when running
27
+ # `rbx compile`.
28
+ warning_stack.get_warning_stack().clear()
29
+
24
30
  console.console.print(
25
31
  f'[success]Compiled file written at [item]{out_path}[/item][/success]'
26
32
  )
27
33
 
28
34
 
29
- def any(path: str):
35
+ def any(path: str, sanitized: bool = False, warnings: bool = False):
30
36
  pkg = package.find_problem_package_or_die()
31
37
 
32
38
  solution = package.get_solution_or_nil(path)
33
39
  if solution is not None:
34
- _compile(solution)
40
+ _compile(
41
+ solution,
42
+ sanitized=SanitizationLevel.FORCE if sanitized else SanitizationLevel.NONE,
43
+ warnings=warnings,
44
+ )
35
45
  return
36
46
 
37
47
  for generator in pkg.generators:
38
48
  if generator.path == pathlib.Path(path) or generator.name == path:
39
- _compile(generator)
49
+ _compile(
50
+ generator,
51
+ sanitized=SanitizationLevel.FORCE
52
+ if sanitized
53
+ else SanitizationLevel.PREFER,
54
+ warnings=warnings,
55
+ )
40
56
  return
41
57
 
42
58
  if pkg.checker is not None and pkg.checker.path == pathlib.Path(path):
43
- _compile(pkg.checker)
59
+ _compile(
60
+ pkg.checker,
61
+ sanitized=SanitizationLevel.FORCE
62
+ if sanitized
63
+ else SanitizationLevel.PREFER,
64
+ warnings=warnings,
65
+ )
44
66
  return
45
67
 
46
68
  if pkg.validator is not None and pkg.validator.path == pathlib.Path(path):
47
- _compile(pkg.validator)
69
+ _compile(
70
+ pkg.validator,
71
+ sanitized=SanitizationLevel.FORCE
72
+ if sanitized
73
+ else SanitizationLevel.PREFER,
74
+ warnings=warnings,
75
+ )
48
76
  return
49
77
 
50
- _compile(CodeItem(path=pathlib.Path(path)))
78
+ _compile(
79
+ CodeItem(path=pathlib.Path(path)),
80
+ sanitized=SanitizationLevel.FORCE if sanitized else SanitizationLevel.NONE,
81
+ warnings=warnings,
82
+ )
@@ -6,7 +6,8 @@ from typing import List, Optional, Tuple
6
6
 
7
7
  import typer
8
8
 
9
- from rbx import console, testing_utils, utils
9
+ from rbx import console, testing_utils
10
+ from rbx.box import cd
10
11
  from rbx.box.contest import contest_utils
11
12
  from rbx.box.contest.contest_package import get_problems
12
13
  from rbx.box.contest.schema import Contest, ContestProblem, ContestStatement
@@ -58,7 +59,7 @@ class ExtractedProblem:
58
59
 
59
60
 
60
61
  def _get_samples(problem: ContestProblem) -> List[Testcase]:
61
- with utils.new_cd(problem.get_path()):
62
+ with cd.new_package_cd(problem.get_path()):
62
63
  contest_utils.clear_package_cache()
63
64
  return get_samples()
64
65
 
@@ -156,7 +157,7 @@ def _build_problem_statements(
156
157
  console.console.print(
157
158
  f'Building statement for problem {extracted_problem.problem.short_name}...'
158
159
  )
159
- with utils.new_cd(extracted_problem.problem.get_path()):
160
+ with cd.new_package_cd(extracted_problem.problem.get_path()):
160
161
  contest_utils.clear_package_cache()
161
162
  # TODO: respect steps override
162
163
  content, _ = build_statements.build_statement_bytes(
@@ -282,6 +283,7 @@ def build_statement_rooted(
282
283
 
283
284
  if joiner is None:
284
285
  return last_content, last_output
286
+ assert statement.joiner is not None
285
287
 
286
288
  # Join statements.
287
289
  console.console.print('Joining statements...')
@@ -6,6 +6,7 @@ import typer
6
6
  from pydantic import ValidationError
7
7
 
8
8
  from rbx import console, utils
9
+ from rbx.box import cd
9
10
  from rbx.box.contest.schema import Contest
10
11
  from rbx.box.package import find_problem_package_or_die, warn_preset_deactivated
11
12
  from rbx.box.schema import Package
@@ -58,7 +59,7 @@ def find_contest(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
58
59
  def within_contest(func):
59
60
  @functools.wraps(func)
60
61
  def wrapper(*args, **kwargs):
61
- with utils.new_cd(find_contest()):
62
+ with cd.new_package_cd(find_contest()):
62
63
  return func(*args, **kwargs)
63
64
 
64
65
  return wrapper
rbx/box/contest/main.py CHANGED
@@ -7,7 +7,7 @@ import rich.prompt
7
7
  import typer
8
8
 
9
9
  from rbx import annotations, console, utils
10
- from rbx.box import creation, presets
10
+ from rbx.box import cd, creation, presets
11
11
  from rbx.box.contest import contest_utils, statements
12
12
  from rbx.box.contest.contest_package import (
13
13
  find_contest,
@@ -111,7 +111,7 @@ def create(
111
111
  if local:
112
112
  shutil.copytree(str(preset_path), str(dest_path / '.local.rbx'))
113
113
 
114
- with utils.new_cd(dest_path):
114
+ with cd.new_package_cd(dest_path):
115
115
  contest_utils.clear_all_caches()
116
116
  presets.generate_lock(preset if not local else presets.LOCAL)
117
117
 
@@ -2,8 +2,8 @@ from typing import Annotated, List, Optional
2
2
 
3
3
  import typer
4
4
 
5
- from rbx import annotations, console, utils
6
- from rbx.box import builder, environment
5
+ from rbx import annotations, console
6
+ from rbx.box import builder, cd, environment
7
7
  from rbx.box.contest import contest_utils
8
8
  from rbx.box.contest.build_contest_statements import build_statement
9
9
  from rbx.box.contest.contest_package import (
@@ -49,7 +49,7 @@ def build(
49
49
  console.console.print(
50
50
  f'Processing problem [item]{problem.short_name}[/item]...'
51
51
  )
52
- with utils.new_cd(problem.get_path()):
52
+ with cd.new_package_cd(problem.get_path()):
53
53
  contest_utils.clear_package_cache()
54
54
 
55
55
  if not builder.build(
rbx/box/deferred.py ADDED
@@ -0,0 +1,26 @@
1
+ from typing import Awaitable, Callable, Generic, Optional, TypeVar
2
+
3
+ T = TypeVar('T')
4
+ U = TypeVar('U')
5
+
6
+
7
+ class Deferred(Generic[T]):
8
+ def __init__(self, func: Callable[[], Awaitable[T]]):
9
+ self.func = func
10
+ self.cache: Optional[T] = None
11
+
12
+ def __call__(self) -> Awaitable[T]:
13
+ async def async_wrapper():
14
+ if self.cache is None:
15
+ self.cache = await self.func()
16
+ return self.cache
17
+
18
+ return async_wrapper()
19
+
20
+ def peek(self) -> Optional[T]:
21
+ return self.cache
22
+
23
+ def wrap_with(
24
+ self, wrapper: Callable[[Awaitable[T]], Awaitable[U]]
25
+ ) -> 'Deferred[U]':
26
+ return Deferred(lambda: wrapper(self()))
rbx/box/extensions.py CHANGED
@@ -5,16 +5,8 @@ from pydantic import BaseModel, Field
5
5
  from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguageExtension
6
6
 
7
7
 
8
- # List of extensions defined in-place.
9
- class MacExtension(BaseModel):
10
- gpp_alternative: Optional[str] = None
11
-
12
-
13
8
  # Extension abstractions.
14
9
  class Extensions(BaseModel):
15
- mac: Optional[MacExtension] = Field(
16
- None, description='Extension for setting mac-only configuration.'
17
- )
18
10
  boca: Optional[BocaExtension] = Field(
19
11
  None, description='Environment-level extensions for BOCA packaging.'
20
12
  )
rbx/box/generators.py CHANGED
@@ -8,7 +8,7 @@ import typer
8
8
 
9
9
  from rbx import console
10
10
  from rbx.box import checkers, package, testcases, validators
11
- from rbx.box.code import compile_item, run_item
11
+ from rbx.box.code import SanitizationLevel, compile_item, run_item
12
12
  from rbx.box.environment import (
13
13
  EnvironmentSandbox,
14
14
  ExecutionConfig,
@@ -32,7 +32,7 @@ from rbx.utils import StatusProgress
32
32
 
33
33
 
34
34
  def _compile_generator(generator: CodeItem) -> str:
35
- return compile_item(generator)
35
+ return compile_item(generator, sanitized=SanitizationLevel.PREFER)
36
36
 
37
37
 
38
38
  def _get_group_input(
@@ -322,7 +322,7 @@ def generate_standalone(
322
322
  # Get generator item
323
323
  generator = package.get_generator(call.name)
324
324
  if generator_digest is None:
325
- generator_digest = compile_item(generator)
325
+ generator_digest = _compile_generator(generator)
326
326
 
327
327
  generation_log = run_item(
328
328
  generator,
@@ -351,7 +351,9 @@ def generate_standalone(
351
351
  # Run validator, if it is available.
352
352
  if validator is not None and validate:
353
353
  if validator_digest is None:
354
- validator_digest = compile_item(validator)
354
+ validator_tp = validators.compile_main_validator()
355
+ assert validator_tp is not None
356
+ _, validator_digest = validator_tp
355
357
  ok, message, *_ = validators.validate_test(output, validator, validator_digest)
356
358
  if not ok:
357
359
  console.console.print(