rbx.cp 0.9.0__py3-none-any.whl → 0.9.2__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/cli.py CHANGED
@@ -159,6 +159,9 @@ def main(
159
159
  state.STATE.debug_logs = capture
160
160
 
161
161
  grading_context.cache_level_var.set(grading_context.CacheLevel(cache))
162
+ grading_context.check_integrity_var.set(
163
+ setter_config.get_setter_config().caching.check_integrity
164
+ )
162
165
 
163
166
  if profile:
164
167
  from rbx.grading import profiling
rbx/box/environment.py CHANGED
@@ -247,13 +247,13 @@ execution config can be individually overridden in the language configuration.""
247
247
 
248
248
 
249
249
  def get_app_environment_path(env: str) -> pathlib.Path:
250
- return config.get_app_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
250
+ return config.get_resources_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
251
251
 
252
252
 
253
253
  def get_active_environment_path() -> pathlib.Path:
254
254
  env_path = presets.get_preset_environment_path()
255
255
  if env_path is None:
256
- env_path = get_app_environment_path(config.get_config().boxEnvironment)
256
+ env_path = get_app_environment_path('default')
257
257
  return env_path
258
258
 
259
259
 
@@ -261,7 +261,7 @@ def get_active_environment_path() -> pathlib.Path:
261
261
  def get_active_environment_description() -> str:
262
262
  env_path = presets.get_preset_environment_path()
263
263
  if env_path is None:
264
- return config.get_config().boxEnvironment
264
+ return 'default'
265
265
  preset = presets.get_active_preset()
266
266
  return f'preset - {preset.name}'
267
267
 
rbx/box/setter_config.py CHANGED
@@ -9,6 +9,7 @@ import typer
9
9
  from pydantic import BaseModel, Field
10
10
 
11
11
  from rbx import config, console, utils
12
+ from rbx.grading.grading_context import CacheLevel
12
13
 
13
14
  app = typer.Typer(no_args_is_help=True)
14
15
 
@@ -52,6 +53,19 @@ class RepeatsConfig(BaseModel):
52
53
  )
53
54
 
54
55
 
56
+ class CachingConfig(BaseModel):
57
+ level: CacheLevel = Field(
58
+ default=CacheLevel.CACHE_ALL,
59
+ description='Whether to enable caching and which caching level to use.',
60
+ )
61
+
62
+ check_integrity: bool = Field(
63
+ default=True,
64
+ description='Whether to check the integrity of the cached result, and evict it'
65
+ 'if file has changed since it was cached.',
66
+ )
67
+
68
+
55
69
  class SetterConfig(BaseModel):
56
70
  sanitizers: SanitizersConfig = Field(
57
71
  default_factory=SanitizersConfig, # type: ignore
@@ -75,6 +89,10 @@ class SetterConfig(BaseModel):
75
89
  default=True,
76
90
  description='Whether to use hyperlinks in the terminal output.',
77
91
  )
92
+ caching: CachingConfig = Field(
93
+ default_factory=CachingConfig, # type: ignore
94
+ description='Configuration for caching.',
95
+ )
78
96
 
79
97
  def substitute_command(self, command: str, sanitized: bool = False) -> str:
80
98
  exe = shlex.split(command)[0]
rbx/config.py CHANGED
@@ -90,6 +90,13 @@ def get_empty_app_persist_path() -> pathlib.Path:
90
90
  return app_dir
91
91
 
92
92
 
93
+ def get_resources_file(path: pathlib.Path) -> pathlib.Path:
94
+ file_path = importlib.resources.files('rbx') / 'resources' / path # type: ignore
95
+ if file_path.is_file():
96
+ return file_path
97
+ raise FileNotFoundError(f'File {path} not found in {_RESOURCES_PKG}.')
98
+
99
+
93
100
  def get_app_file(path: pathlib.Path) -> pathlib.Path:
94
101
  file_path = get_app_path() / path
95
102
  if file_path.is_file():
rbx/grading/caching.py CHANGED
@@ -15,7 +15,12 @@ from rbx.grading.judge.cacher import FileCacher
15
15
  from rbx.grading.judge.digester import digest_cooperatively
16
16
  from rbx.grading.judge.storage import copyfileobj
17
17
  from rbx.grading.profiling import Profiler
18
- from rbx.grading.steps import DigestHolder, GradingArtifacts, GradingLogsHolder
18
+ from rbx.grading.steps import (
19
+ DigestHolder,
20
+ GradingArtifacts,
21
+ GradingFileOutput,
22
+ GradingLogsHolder,
23
+ )
19
24
 
20
25
  VERBOSE = False
21
26
 
@@ -109,10 +114,27 @@ def _build_fingerprint_list(
109
114
  return fingerprints
110
115
 
111
116
 
117
+ def _maybe_check_integrity(output: GradingFileOutput):
118
+ if not grading_context.should_check_integrity():
119
+ return
120
+ if output.dest is None or not output.dest.is_symlink():
121
+ return
122
+ if output.digest is None or output.digest.value is None:
123
+ return
124
+ with output.dest.open('rb') as f:
125
+ fingerprint = digest_cooperatively(f)
126
+ if fingerprint != output.digest.value:
127
+ raise ValueError(
128
+ f'Cache was tampered with, file {output.dest} has changed since it was cached.\nPlease run `rbx clean` to reset the cache.'
129
+ )
130
+
131
+
112
132
  def _build_output_fingerprint_list(artifacts_list: List[GradingArtifacts]) -> List[str]:
113
133
  fingerprints = []
114
134
  for artifacts in artifacts_list:
115
135
  for output in artifacts.outputs:
136
+ if output.hash:
137
+ _maybe_check_integrity(output)
116
138
  if output.dest is None or output.intermediate or output.hash:
117
139
  continue
118
140
  if not output.dest.is_file():
@@ -313,7 +335,7 @@ class DependencyCache:
313
335
  self.cacher = cacher
314
336
  self.db = shelve.open(self._cache_name())
315
337
  tmp_dir = pathlib.Path(tempfile.mkdtemp())
316
- self.transient_db = shelve.open(tmp_dir / '.cache_db')
338
+ self.transient_db = shelve.open(str(tmp_dir / '.cache_db'))
317
339
  atexit.register(lambda: self.db.close())
318
340
  atexit.register(lambda: self.transient_db.close())
319
341
  atexit.register(lambda: shutil.rmtree(tmp_dir))
rbx/grading/conftest.py CHANGED
@@ -28,6 +28,6 @@ def sandbox(request, file_cacher: FileCacher) -> Iterator[SandboxBase]:
28
28
 
29
29
  @pytest.fixture
30
30
  def dependency_cache(
31
- request, cleandir: pathlib.Path, storage: Storage
31
+ request, cleandir: pathlib.Path, file_cacher: FileCacher
32
32
  ) -> Iterator[DependencyCache]:
33
- yield DependencyCache(cleandir / '.box', storage)
33
+ yield DependencyCache(cleandir / '.box', file_cacher)
@@ -1,7 +1,8 @@
1
1
  import contextvars
2
- from enum import Enum
3
2
  from typing import Callable, Optional, Union
4
3
 
4
+ from rbx.autoenum import AutoEnum, alias
5
+
5
6
  Condition = Union[bool, Callable[[], bool]]
6
7
 
7
8
 
@@ -15,11 +16,11 @@ class ConditionedContext:
15
16
  return self.when()
16
17
 
17
18
 
18
- class CacheLevel(Enum):
19
- NO_CACHE = 0
20
- CACHE_TRANSIENTLY = 1
21
- CACHE_COMPILATION = 2
22
- CACHE_ALL = 3
19
+ class CacheLevel(AutoEnum):
20
+ NO_CACHE = alias('none')
21
+ CACHE_TRANSIENTLY = alias('transient')
22
+ CACHE_COMPILATION = alias('compilation')
23
+ CACHE_ALL = alias('all')
23
24
 
24
25
 
25
26
  cache_level_var = contextvars.ContextVar('cache_level', default=CacheLevel.CACHE_ALL)
@@ -30,11 +31,11 @@ def is_compilation_only() -> bool:
30
31
 
31
32
 
32
33
  def is_transient() -> bool:
33
- return cache_level_var.get().value <= CacheLevel.CACHE_TRANSIENTLY.value
34
+ return cache_level_var.get() == CacheLevel.CACHE_TRANSIENTLY or is_no_cache()
34
35
 
35
36
 
36
37
  def is_no_cache() -> bool:
37
- return cache_level_var.get().value <= CacheLevel.NO_CACHE.value
38
+ return cache_level_var.get() == CacheLevel.NO_CACHE
38
39
 
39
40
 
40
41
  class cache_level(ConditionedContext):
@@ -94,3 +95,28 @@ class compression(ConditionedContext):
94
95
  if self.use_compression_token is not None:
95
96
  use_compression_var.reset(self.use_compression_token)
96
97
  return None
98
+
99
+
100
+ check_integrity_var = contextvars.ContextVar('check_integrity', default=True)
101
+
102
+
103
+ def should_check_integrity() -> bool:
104
+ return check_integrity_var.get()
105
+
106
+
107
+ class check_integrity(ConditionedContext):
108
+ def __init__(self, enabled: bool, when: Condition = True):
109
+ super().__init__(when)
110
+ self.enabled = enabled
111
+ self.token = None
112
+
113
+ def __enter__(self):
114
+ if not self.should_enter():
115
+ return self
116
+ self.token = check_integrity_var.set(self.enabled)
117
+ return self
118
+
119
+ def __exit__(self, exc_type, exc_val, exc_tb):
120
+ if self.token is not None:
121
+ check_integrity_var.reset(self.token)
122
+ return None
@@ -2,9 +2,12 @@ import os
2
2
  import pathlib
3
3
  import sys
4
4
 
5
- from rbx.grading import steps_with_caching
5
+ import pytest
6
+
7
+ from rbx.grading import grading_context, steps_with_caching
6
8
  from rbx.grading.caching import DependencyCache
7
9
  from rbx.grading.judge.cacher import FileCacher
10
+ from rbx.grading.judge.digester import digest_cooperatively
8
11
  from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
9
12
  from rbx.grading.steps import (
10
13
  DigestOrSource,
@@ -74,6 +77,124 @@ async def test_run_from_disk(
74
77
  assert not artifacts.logs.cached
75
78
 
76
79
 
80
+ async def test_run_reruns_if_cache_disabled(
81
+ cleandir: pathlib.Path,
82
+ dependency_cache: DependencyCache,
83
+ sandbox: SandboxBase,
84
+ file_cacher: FileCacher,
85
+ ):
86
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
87
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
88
+ artifacts = GradingArtifacts()
89
+ artifacts.inputs.append(
90
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
91
+ )
92
+ artifacts.outputs.append(
93
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
94
+ )
95
+ with grading_context.cache_level(grading_context.CacheLevel.NO_CACHE):
96
+ await steps_with_caching.run(
97
+ f'{sys.executable} executable.py',
98
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
99
+ sandbox=sandbox,
100
+ artifacts=artifacts,
101
+ dependency_cache=dependency_cache,
102
+ )
103
+ return artifacts
104
+
105
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
106
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
107
+ assert artifacts.logs is not None
108
+ assert not artifacts.logs.cached
109
+
110
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
111
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
112
+ assert another_artifacts.logs is not None
113
+ assert not another_artifacts.logs.cached
114
+
115
+
116
+ async def test_run_reruns_if_first_run_cache_disabled(
117
+ cleandir: pathlib.Path,
118
+ dependency_cache: DependencyCache,
119
+ sandbox: SandboxBase,
120
+ file_cacher: FileCacher,
121
+ ):
122
+ async def configure_and_run_with_dest(
123
+ dest: pathlib.Path, cache: bool = True
124
+ ) -> GradingArtifacts:
125
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
126
+ artifacts = GradingArtifacts()
127
+ artifacts.inputs.append(
128
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
129
+ )
130
+ artifacts.outputs.append(
131
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
132
+ )
133
+ with grading_context.cache_level(
134
+ grading_context.CacheLevel.NO_CACHE, when=cache
135
+ ):
136
+ await steps_with_caching.run(
137
+ f'{sys.executable} executable.py',
138
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
139
+ sandbox=sandbox,
140
+ artifacts=artifacts,
141
+ dependency_cache=dependency_cache,
142
+ )
143
+ return artifacts
144
+
145
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'), cache=False)
146
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
147
+ assert artifacts.logs is not None
148
+ assert not artifacts.logs.cached
149
+
150
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
151
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
152
+ assert another_artifacts.logs is not None
153
+ assert not another_artifacts.logs.cached
154
+
155
+
156
+ async def test_run_reruns_if_second_run_cache_disabled(
157
+ cleandir: pathlib.Path,
158
+ dependency_cache: DependencyCache,
159
+ sandbox: SandboxBase,
160
+ file_cacher: FileCacher,
161
+ ):
162
+ async def configure_and_run_with_dest(
163
+ dest: pathlib.Path, cache: bool = True
164
+ ) -> GradingArtifacts:
165
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
166
+ artifacts = GradingArtifacts()
167
+ artifacts.inputs.append(
168
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
169
+ )
170
+ artifacts.outputs.append(
171
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
172
+ )
173
+ with grading_context.cache_level(
174
+ grading_context.CacheLevel.NO_CACHE, when=cache
175
+ ):
176
+ await steps_with_caching.run(
177
+ f'{sys.executable} executable.py',
178
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
179
+ sandbox=sandbox,
180
+ artifacts=artifacts,
181
+ dependency_cache=dependency_cache,
182
+ )
183
+ return artifacts
184
+
185
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
186
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
187
+ assert artifacts.logs is not None
188
+ assert not artifacts.logs.cached
189
+
190
+ another_artifacts = await configure_and_run_with_dest(
191
+ pathlib.Path('out.txt'), cache=False
192
+ )
193
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
194
+ assert another_artifacts.logs is not None
195
+ assert not another_artifacts.logs.cached
196
+
197
+
77
198
  async def test_run_caches_intermediate_digest_if_dest_changes(
78
199
  cleandir: pathlib.Path,
79
200
  dependency_cache: DependencyCache,
@@ -106,12 +227,127 @@ async def test_run_caches_intermediate_digest_if_dest_changes(
106
227
  another_artifacts = await configure_and_run_with_dest(
107
228
  pathlib.Path('another-out.txt')
108
229
  )
230
+ assert (cleandir / 'another-out.txt').read_text().strip() == '5'
231
+ assert another_artifacts.logs is not None
232
+ assert another_artifacts.logs.cached
233
+
234
+
235
+ async def test_run_caches_intermediate_digest_if_dest_changes_and_cache_transient(
236
+ cleandir: pathlib.Path,
237
+ dependency_cache: DependencyCache,
238
+ sandbox: SandboxBase,
239
+ file_cacher: FileCacher,
240
+ ):
241
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
242
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
243
+ artifacts = GradingArtifacts()
244
+ artifacts.inputs.append(
245
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
246
+ )
247
+ artifacts.outputs.append(
248
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
249
+ )
250
+
251
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
252
+ await steps_with_caching.run(
253
+ f'{sys.executable} executable.py',
254
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
255
+ sandbox=sandbox,
256
+ artifacts=artifacts,
257
+ dependency_cache=dependency_cache,
258
+ )
259
+ return artifacts
260
+
261
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
262
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
263
+ assert artifacts.logs is not None
264
+ assert not artifacts.logs.cached
265
+
266
+ another_artifacts = await configure_and_run_with_dest(
267
+ pathlib.Path('another-out.txt')
268
+ )
269
+ assert (cleandir / 'another-out.txt').read_text().strip() == '5'
270
+ assert another_artifacts.logs is not None
271
+ assert another_artifacts.logs.cached
272
+
273
+
274
+ async def test_run_overwrites_changed_file_when_storage_value_is_changed_and_cache_transient(
275
+ cleandir: pathlib.Path,
276
+ dependency_cache: DependencyCache,
277
+ sandbox: SandboxBase,
278
+ file_cacher: FileCacher,
279
+ ):
280
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
281
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
282
+ artifacts = GradingArtifacts()
283
+ artifacts.inputs.append(
284
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
285
+ )
286
+ artifacts.outputs.append(
287
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
288
+ )
289
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
290
+ await steps_with_caching.run(
291
+ f'{sys.executable} executable.py',
292
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
293
+ sandbox=sandbox,
294
+ artifacts=artifacts,
295
+ dependency_cache=dependency_cache,
296
+ )
297
+ return artifacts
298
+
299
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
300
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
301
+ assert artifacts.logs is not None
302
+ assert not artifacts.logs.cached
303
+
304
+ pathlib.Path('out.txt').write_text('42')
305
+
306
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
307
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
308
+ assert another_artifacts.logs is not None
309
+ assert another_artifacts.logs.cached
310
+
311
+
312
+ async def test_run_overwrites_changed_file_when_file_deleted_and_cache_transient(
313
+ cleandir: pathlib.Path,
314
+ dependency_cache: DependencyCache,
315
+ sandbox: SandboxBase,
316
+ file_cacher: FileCacher,
317
+ ):
318
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
319
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
320
+ artifacts = GradingArtifacts()
321
+ artifacts.inputs.append(
322
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
323
+ )
324
+ artifacts.outputs.append(
325
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
326
+ )
327
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
328
+ await steps_with_caching.run(
329
+ f'{sys.executable} executable.py',
330
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
331
+ sandbox=sandbox,
332
+ artifacts=artifacts,
333
+ dependency_cache=dependency_cache,
334
+ )
335
+ return artifacts
336
+
337
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
338
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
339
+ assert artifacts.logs is not None
340
+ assert not artifacts.logs.cached
341
+
342
+ pathlib.Path('out.txt').unlink()
343
+
344
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
109
345
  assert (cleandir / 'out.txt').read_text().strip() == '5'
110
346
  assert another_artifacts.logs is not None
111
347
  assert another_artifacts.logs.cached
112
348
 
113
349
 
114
- async def test_run_overwrite_changed_file_with_storage_value(
350
+ async def test_run_fails_when_storage_value_is_changed_and_integrity_check_is_enabled(
115
351
  cleandir: pathlib.Path,
116
352
  dependency_cache: DependencyCache,
117
353
  sandbox: SandboxBase,
@@ -142,13 +378,50 @@ async def test_run_overwrite_changed_file_with_storage_value(
142
378
 
143
379
  pathlib.Path('out.txt').write_text('42')
144
380
 
381
+ with pytest.raises(ValueError) as exc_info:
382
+ await configure_and_run_with_dest(pathlib.Path('out.txt'))
383
+ assert 'rbx clean' in str(exc_info.value)
384
+ assert (cleandir / 'out.txt').read_text().strip() == '42'
385
+
386
+
387
+ async def test_run_evicts_and_recreates_deleted_file_with_storage_value(
388
+ cleandir: pathlib.Path,
389
+ dependency_cache: DependencyCache,
390
+ sandbox: SandboxBase,
391
+ file_cacher: FileCacher,
392
+ ):
393
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
394
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
395
+ artifacts = GradingArtifacts()
396
+ artifacts.inputs.append(
397
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
398
+ )
399
+ artifacts.outputs.append(
400
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
401
+ )
402
+ await steps_with_caching.run(
403
+ f'{sys.executable} executable.py',
404
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
405
+ sandbox=sandbox,
406
+ artifacts=artifacts,
407
+ dependency_cache=dependency_cache,
408
+ )
409
+ return artifacts
410
+
411
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
412
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
413
+ assert artifacts.logs is not None
414
+ assert not artifacts.logs.cached
415
+
416
+ pathlib.Path('out.txt').unlink()
417
+
145
418
  another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
146
419
  assert (cleandir / 'out.txt').read_text().strip() == '5'
147
420
  assert another_artifacts.logs is not None
148
421
  assert another_artifacts.logs.cached
149
422
 
150
423
 
151
- async def test_run_recreates_deleted_file_with_storage_value(
424
+ async def test_run_evicts_when_storage_value_deleted(
152
425
  cleandir: pathlib.Path,
153
426
  dependency_cache: DependencyCache,
154
427
  sandbox: SandboxBase,
@@ -177,12 +450,15 @@ async def test_run_recreates_deleted_file_with_storage_value(
177
450
  assert artifacts.logs is not None
178
451
  assert not artifacts.logs.cached
179
452
 
180
- pathlib.Path('out.txt').unlink()
453
+ # Delete the file from the cache
454
+ with pathlib.Path('out.txt').open('rb') as f:
455
+ digest = digest_cooperatively(f)
456
+ file_cacher.delete(digest)
181
457
 
182
458
  another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
183
459
  assert (cleandir / 'out.txt').read_text().strip() == '5'
184
460
  assert another_artifacts.logs is not None
185
- assert another_artifacts.logs.cached
461
+ assert not another_artifacts.logs.cached
186
462
 
187
463
 
188
464
  async def test_run_overwrite_exec_bit_when_changed(
@@ -14,6 +14,21 @@ command_substitutions:
14
14
  python2: python2
15
15
  python3: python3
16
16
 
17
+ caching:
18
+ # Set the caching level.
19
+ #
20
+ # CACHE_ALL: Cache everything.
21
+ # CACHE_COMPILATION: Cache only compilation.
22
+ # NO_CACHE: Do not cache anything.
23
+ level: CACHE_ALL
24
+
25
+ # Whether to check the integrity of the cached result, and evict it
26
+ # if file has changed since it was cached.
27
+ #
28
+ # Disable for more performance, but be careful to not modify any generated
29
+ # files.
30
+ check_integrity: true
31
+
17
32
  repeats:
18
33
  # Number of times to run the solution.
19
34
  reps: 1
@@ -14,6 +14,21 @@ command_substitutions:
14
14
  python2: python2
15
15
  python3: python3
16
16
 
17
+ caching:
18
+ # Set the caching level.
19
+ #
20
+ # CACHE_ALL: Cache everything.
21
+ # CACHE_COMPILATION: Cache only compilation.
22
+ # NO_CACHE: Do not cache anything.
23
+ level: CACHE_ALL
24
+
25
+ # Whether to check the integrity of the cached result, and evict it
26
+ # if file has changed since it was cached.
27
+ #
28
+ # Disable for more performance, but be careful to not modify any generated
29
+ # files.
30
+ check_integrity: true
31
+
17
32
  repeats:
18
33
  # Number of times to run the solution.
19
34
  reps: 1
@@ -3,30 +3,37 @@ sandbox: "stupid"
3
3
  defaultCompilation:
4
4
  sandbox:
5
5
  maxProcesses: 1000
6
- timeLimit: 10000 # 10 seconds
7
- wallTimeLimit: 10000 # 10 seconds
6
+ timeLimit: 50000 # 50 seconds
7
+ wallTimeLimit: 50000 # 50 seconds
8
8
  memoryLimit: 1024 # 1gb
9
- preserveEnv: true
10
- mirrorDirs:
11
- - "/etc"
12
- - "/usr"
13
9
  defaultExecution:
14
10
  sandbox:
15
11
  # Useful for checkers, validators, etc.
16
- timeLimit: 10000 # 10 seconds
17
- wallTimeLimit: 10000 # 10 seconds
12
+ timeLimit: 50000 # 50 seconds
13
+ wallTimeLimit: 50000 # 50 seconds
18
14
  memoryLimit: 1024 # 1gb
19
15
  languages:
20
16
  - name: "cpp"
21
- readableName: "C++17"
17
+ readableName: "C++20"
22
18
  extension: "cpp"
23
19
  compilation:
24
- commands:
25
- - "g++ -std=c++17 -O2 -o {executable} {compilable}"
20
+ commands: ["g++ -std=c++20 -O2 -o {executable} {compilable}"]
26
21
  execution:
27
22
  command: "./{executable}"
28
23
  fileMapping:
29
24
  compilable: "compilable.cpp"
25
+ extensions:
26
+ boca:
27
+ bocaLanguage: "cc"
28
+ - name: "c"
29
+ readableName: "C"
30
+ extension: "c"
31
+ compilation:
32
+ commands: ["gcc -std=gnu11 -O2 -lm -o {executable} {compilable}"]
33
+ execution:
34
+ command: "./{executable}"
35
+ fileMapping:
36
+ compilable: "compilable.c"
30
37
  - name: "py"
31
38
  readableName: "Python3"
32
39
  extension: "py"
@@ -34,3 +41,27 @@ languages:
34
41
  command: "python3 {executable}"
35
42
  fileMapping:
36
43
  executable: "executable.py"
44
+ extensions:
45
+ boca:
46
+ bocaLanguage: "py3"
47
+ - name: "java"
48
+ readableName: "Java"
49
+ extension: "java"
50
+ compilation:
51
+ commands:
52
+ - "javac -Xlint -encoding UTF-8 {compilable}"
53
+ - "jar cvf {executable} @glob:*.class"
54
+ execution:
55
+ command:
56
+ "java -Xss100m -Xmx{{memory}}m -Xms{{initialMemory}}m -cp {executable}
57
+ Main"
58
+ fileMapping:
59
+ compilable: "Main.java"
60
+ executable: "Main.jar"
61
+ extensions:
62
+ boca:
63
+ languages: ["c", "cc", "java", "py3"]
64
+ flags:
65
+ c: "-O2 -lm -static"
66
+ cc: "-std=c++20 -O2 -lm -static"
67
+ preferContestLetter: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -5,7 +5,7 @@ rbx/box/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  rbx/box/builder.py,sha256=MDm2qqmhedAbhn3rWP6cDwbBsGhV6sz_2sg1zLkPDw0,3613
6
6
  rbx/box/cd.py,sha256=_XAzb3kV1NUaaRs8hc9SGDo10O1yh2_gr1EiAKzfUjI,2711
7
7
  rbx/box/checkers.py,sha256=2eLfMOzJajoca26M4rOBxUxve3-BpxcMu_q2upI1Pcg,13017
8
- rbx/box/cli.py,sha256=fF8uPSqW_tBuWbiU0WnOPU5inUe3rV6XnLk2MYbxBYQ,29638
8
+ rbx/box/cli.py,sha256=yksizw3ukyQL4xcF1oRlIR8Zt5JMqxAul7Y1T206Kwo,29755
9
9
  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
@@ -20,7 +20,7 @@ rbx/box/creation.py,sha256=I3sclB5WTK_pcXDnYU6LEbvGVSHwRS_30L3GVAz-QqM,1369
20
20
  rbx/box/deferred.py,sha256=II3X9e87JCOZtmspnHh-n4PFqh-FsH_oc0XJHZ9ZYVQ,691
21
21
  rbx/box/download.py,sha256=tLW5gLVeLk0gHMEMwScSoHIXQPkXuPsqXzItsrsnUZY,3070
22
22
  rbx/box/dump_schemas.py,sha256=3j5t47_vJmXj0BCczxDX6ByOcsfolGEDNCBXlPpk86w,593
23
- rbx/box/environment.py,sha256=399qt1kdMRXEQWzGt7m-fj6eAGk112TUM0YXm5LvTzw,13618
23
+ 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
@@ -58,7 +58,7 @@ 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
60
  rbx/box/schema.py,sha256=424VcpZzY2qUz8rlETwIqrzzwmnoARILEO7Wf2P61BY,18855
61
- rbx/box/setter_config.py,sha256=9iObg6BwxQhFAhIOk31Jc0BDDpRYVGf3SyLIOsWIltM,4393
61
+ rbx/box/setter_config.py,sha256=WHlWjjdHNoBbCdZqYokctTSxvtPqQXGmrgDHyaK4FKk,4985
62
62
  rbx/box/solutions.py,sha256=I8DqQ4HDO_MWlabqJHnCgG7udDgUgox15YW5TCrN-8o,53864
63
63
  rbx/box/solutions_test.py,sha256=PX1TQoRzNd9mi1SGsG7WFrpqFgNrNX5Kwt0mkwFdoOA,1749
64
64
  rbx/box/state.py,sha256=MMf3DvfQji0jKEliCHct2Tpp_0epL1tvP8HbHNArQIc,166
@@ -115,16 +115,16 @@ rbx/box/validators.py,sha256=oqlNhw7jivbbH5l8g3xwihPRy76AM7MA3G4A8nyI_V0,10416
115
115
  rbx/box/validators_test.py,sha256=WY4Ho-wlsPHc0YNuz0KFVd6KQ9ouuiou3w5_zMOZ4Fs,362
116
116
  rbx/checker.py,sha256=pj1jO3my48ru-qugbER5onccANCjoR0-PaFe3H3VGEY,4118
117
117
  rbx/clone.py,sha256=wpHyED0_7ST7LD3vj7HjXhzqEzlwh6dRQvKQVDYhGeU,6744
118
- rbx/config.py,sha256=0Mzlh5Ud4Ju7o7uyyRYhQpg8sMP7psUns_xlou6Bopk,8187
118
+ rbx/config.py,sha256=Tj0NHSf13fXxbNpif5C4qnaL1k3S-G87OnzuykEAcNQ,8463
119
119
  rbx/conftest.py,sha256=ouilbOIpvX8jTEdCAiWT85CbdBQKUUf41BjmDI82u-Y,967
120
120
  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=T86-Q5-VVhXSSF0wC6JoH1kRFKL03l0l_Qf0unWaTE0,14611
125
- rbx/grading/conftest.py,sha256=iN9LUG1IQqhK5JjkctcP68v6675oYsiD2sQSgyLMTqw,960
124
+ rbx/grading/caching.py,sha256=X20v-ZCmcT6OXvWzq2VBdACi6_cEYWB2phkiJ34CNeg,15306
125
+ rbx/grading/conftest.py,sha256=820Uw3AE-dwAfwLhXjgq_PixpDI1-JEXsOPYf4VT5H8,971
126
126
  rbx/grading/debug_context.py,sha256=kuAXEI8yRG8xfhS9WKKIRh9X0e5JUD8zvl_cpczJTC8,699
127
- rbx/grading/grading_context.py,sha256=VRGlGSyyNQfEuV_E0f5-P7NKb6nCzjplNFmFRBmvWwE,2665
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
@@ -141,7 +141,7 @@ rbx/grading/processing_context.py,sha256=Jg9kNnkH3hi2hiE6Gh23QwS89r9Zj230NMl1CUE
141
141
  rbx/grading/profiling.py,sha256=OEdtoAzjYjLfi-QI5Ke7tLZzJeqvGpMB2utQBNuH3E4,3369
142
142
  rbx/grading/steps.py,sha256=ChDsSMkSwMjT1NiLe3sBhXuzwuxeLWJjvne9BnwBpDQ,30127
143
143
  rbx/grading/steps_with_caching.py,sha256=b6NVZN46IG2r49uQ6u19Jj6AJ_nQFO6_pcKq4dWTwuQ,4579
144
- rbx/grading/steps_with_caching_run_test.py,sha256=mh4DRInrOGhnQFWD1SlcjDm_HvcSDFTDMSpAlG-Q5SI,15570
144
+ rbx/grading/steps_with_caching_run_test.py,sha256=Ldrs1VS01TWYpqg1x9LeiF7_TejsE_suL3y1MI5pAlU,26599
145
145
  rbx/grading_utils.py,sha256=lL2KtSkOsMElqrRoApQTbFcqVOeHVWUDTMCa3IsLpC4,4484
146
146
  rbx/hydration.py,sha256=WqbgIfCZNwqspVhMuUlx8-sNOYhq_ZWbeZnkcetuAZI,3669
147
147
  rbx/main.py,sha256=pFZreQhcoq-vIvWuVVeJEa8s_ka24dxF0WmaasarscE,2865
@@ -152,9 +152,9 @@ rbx/providers/provider.py,sha256=CNRB-uJZkNFIWv8xhW2s8PY9EwUSK8Ey1Yvxk4YLvcg,688
152
152
  rbx/resources/checkers/boilerplate.cpp,sha256=vj1Qjy59JKEzb4ZpaX_MkL1FaZn_tTLZXjrIkP0nGfc,363
153
153
  rbx/resources/checkers/noop.cpp,sha256=aZI6EfZEWq2opbb-kysvl0UeUoYGLU682LccP7cfBG8,214
154
154
  rbx/resources/default_config.json,sha256=8GZVHns4nci0-e5ALk9C1lfO6TO9W2ZlmZtxHkL6ibA,949
155
- rbx/resources/default_setter_config.mac.yml,sha256=i28xwAUDg-kuxx19knCiYkh-NR7QeYen5e0s2BdkpYg,1029
156
- rbx/resources/default_setter_config.yml,sha256=oy6fbuQyYaJS2Cw2zcbYcBBGt6138CyB3-bLl45_QqY,1000
157
- rbx/resources/envs/default.rbx.yml,sha256=656E1xgBEHluvNJp2p7miiqEO0XHkpZTVcHlEDRq8BI,850
155
+ rbx/resources/default_setter_config.mac.yml,sha256=UWRvdsFp-6I9dU1b1q9hYNAday9TXvyVd_5gquIkcFQ,1438
156
+ rbx/resources/default_setter_config.yml,sha256=cYTiQqxadRyOmgI9aS6LlG3Cn-azKhOmaZlpZBXvBow,1409
157
+ rbx/resources/envs/default.rbx.yml,sha256=quSPG5Xs9KroYLATNLPNtORLGRWtrLLt2Fx81T1enAM,1692
158
158
  rbx/resources/envs/isolate.rbx.yml,sha256=iY7wHco2JYN0LEmZ-ErmjKLz7beyNMUxRkSbhB-Y6bY,867
159
159
  rbx/resources/packagers/boca/checker.sh,sha256=kesoIYeCttAtuQBq-7HvfXK8cAFTJhFbqHm9rNd_udg,1272
160
160
  rbx/resources/packagers/boca/compare.sh,sha256=a6AmQqwrIq616DHjp_S2l95wO7UiMVy0LQ9EajZKDDk,1201
@@ -239,8 +239,8 @@ 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
241
  rbx/utils.py,sha256=YvN0q1vaLR9HxUfXKOTx1iN6Bp_fI8YNTQiJ8TNq7eM,4957
242
- rbx_cp-0.9.0.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
243
- rbx_cp-0.9.0.dist-info/METADATA,sha256=_OdX8Y5IpkjxslvvZ45cTWmL6gpqZ7CiRnT2jlkmqlk,4586
244
- rbx_cp-0.9.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
245
- rbx_cp-0.9.0.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
246
- rbx_cp-0.9.0.dist-info/RECORD,,
242
+ rbx_cp-0.9.2.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
243
+ rbx_cp-0.9.2.dist-info/METADATA,sha256=-X0CkdEeIPf0Xnt25cpKEVfp49-N3GN4f_CG5lmmRG0,4586
244
+ rbx_cp-0.9.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
245
+ rbx_cp-0.9.2.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
246
+ rbx_cp-0.9.2.dist-info/RECORD,,
File without changes