rbx.cp 0.13.2__py3-none-any.whl → 0.13.4__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/annotations.py +5 -5
- rbx/box/checkers.py +24 -13
- rbx/box/cli.py +1 -4
- rbx/box/contest/build_contest_statements.py +16 -3
- rbx/box/contest/schema.py +1 -2
- rbx/box/fields.py +25 -1
- rbx/box/generators.py +5 -2
- rbx/box/global_package.py +5 -1
- rbx/box/header.py +19 -11
- rbx/box/package.py +3 -1
- rbx/box/presets/__init__.py +2 -2
- rbx/box/schema.py +4 -25
- rbx/box/statements/build_statements.py +5 -1
- rbx/box/statements/builders.py +7 -7
- rbx/box/statements/schema.py +11 -2
- rbx/box/testcase_utils.py +2 -0
- rbx/box/testing/__init__.py +0 -0
- rbx/box/testing/testing_package.py +241 -0
- rbx/box/testing/testing_preset.py +36 -0
- rbx/box/testing/testing_shared.py +81 -0
- rbx/box/tooling/main.py +2 -1
- rbx/box/validators.py +2 -1
- rbx/grading/caching.py +3 -2
- rbx/resources/presets/default/shared/contest_template.rbx.tex +1 -1
- rbx/resources/presets/default/shared/problem_template.rbx.tex +5 -1
- rbx/testing_utils.py +1 -1
- rbx/utils.py +8 -0
- {rbx_cp-0.13.2.dist-info → rbx_cp-0.13.4.dist-info}/METADATA +2 -1
- {rbx_cp-0.13.2.dist-info → rbx_cp-0.13.4.dist-info}/RECORD +32 -50
- rbx/box/conftest.py +0 -42
- rbx/box/generators_test.py +0 -67
- rbx/box/lazy_importing_test.py +0 -25
- rbx/box/solutions_test.py +0 -47
- rbx/box/validators_test.py +0 -15
- rbx/checker.py +0 -128
- rbx/clone.py +0 -197
- rbx/conftest.py +0 -38
- rbx/create.py +0 -37
- rbx/edit.py +0 -24
- rbx/grading/conftest.py +0 -33
- rbx/grading/steps_with_caching_run_test.py +0 -707
- rbx/grading_utils.py +0 -148
- rbx/hydration.py +0 -101
- rbx/main.py +0 -118
- rbx/metadata.py +0 -105
- rbx/run.py +0 -45
- rbx/schema.py +0 -64
- rbx/submit.py +0 -61
- rbx/test.py +0 -349
- rbx/testcase.py +0 -70
- rbx/testcase_rendering.py +0 -79
- {rbx_cp-0.13.2.dist-info → rbx_cp-0.13.4.dist-info}/LICENSE +0 -0
- {rbx_cp-0.13.2.dist-info → rbx_cp-0.13.4.dist-info}/WHEEL +0 -0
- {rbx_cp-0.13.2.dist-info → rbx_cp-0.13.4.dist-info}/entry_points.txt +0 -0
@@ -1,707 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import pathlib
|
3
|
-
import sys
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
|
7
|
-
from rbx.grading import grading_context, steps_with_caching
|
8
|
-
from rbx.grading.caching import DependencyCache
|
9
|
-
from rbx.grading.judge.cacher import FileCacher
|
10
|
-
from rbx.grading.judge.digester import digest_cooperatively
|
11
|
-
from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
|
12
|
-
from rbx.grading.steps import (
|
13
|
-
DigestOrSource,
|
14
|
-
GradingArtifacts,
|
15
|
-
GradingFileInput,
|
16
|
-
GradingFileOutput,
|
17
|
-
RunLogMetadata,
|
18
|
-
)
|
19
|
-
|
20
|
-
|
21
|
-
async def test_run_from_digest(
|
22
|
-
cleandir: pathlib.Path,
|
23
|
-
dependency_cache: DependencyCache,
|
24
|
-
sandbox: SandboxBase,
|
25
|
-
file_cacher: FileCacher,
|
26
|
-
):
|
27
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
28
|
-
artifacts = GradingArtifacts()
|
29
|
-
artifacts.inputs.append(
|
30
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
31
|
-
)
|
32
|
-
artifacts.outputs.append(
|
33
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=pathlib.Path('out.txt'))
|
34
|
-
)
|
35
|
-
await steps_with_caching.run(
|
36
|
-
f'{sys.executable} executable.py',
|
37
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
38
|
-
sandbox=sandbox,
|
39
|
-
artifacts=artifacts,
|
40
|
-
dependency_cache=dependency_cache,
|
41
|
-
metadata=RunLogMetadata(),
|
42
|
-
)
|
43
|
-
|
44
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
45
|
-
assert artifacts.logs is not None
|
46
|
-
assert artifacts.logs.run is not None
|
47
|
-
assert artifacts.logs.run.metadata is not None
|
48
|
-
assert not artifacts.logs.cached
|
49
|
-
|
50
|
-
|
51
|
-
async def test_run_from_disk(
|
52
|
-
cleandir: pathlib.Path,
|
53
|
-
dependency_cache: DependencyCache,
|
54
|
-
sandbox: SandboxBase,
|
55
|
-
):
|
56
|
-
pathlib.Path('executable.py').write_text('print(42)')
|
57
|
-
|
58
|
-
executable = DigestOrSource.create(pathlib.Path('executable.py'))
|
59
|
-
artifacts = GradingArtifacts()
|
60
|
-
artifacts.inputs.append(
|
61
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
62
|
-
)
|
63
|
-
artifacts.outputs.append(
|
64
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=pathlib.Path('out.txt'))
|
65
|
-
)
|
66
|
-
await steps_with_caching.run(
|
67
|
-
f'{sys.executable} executable.py',
|
68
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
69
|
-
sandbox=sandbox,
|
70
|
-
artifacts=artifacts,
|
71
|
-
dependency_cache=dependency_cache,
|
72
|
-
)
|
73
|
-
|
74
|
-
assert (cleandir / 'out.txt').read_text().strip() == '42'
|
75
|
-
assert artifacts.logs is not None
|
76
|
-
assert artifacts.logs.run is not None
|
77
|
-
assert not artifacts.logs.cached
|
78
|
-
|
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
|
-
|
198
|
-
async def test_run_caches_intermediate_digest_if_dest_changes(
|
199
|
-
cleandir: pathlib.Path,
|
200
|
-
dependency_cache: DependencyCache,
|
201
|
-
sandbox: SandboxBase,
|
202
|
-
file_cacher: FileCacher,
|
203
|
-
):
|
204
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
205
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
206
|
-
artifacts = GradingArtifacts()
|
207
|
-
artifacts.inputs.append(
|
208
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
209
|
-
)
|
210
|
-
artifacts.outputs.append(
|
211
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
|
212
|
-
)
|
213
|
-
await steps_with_caching.run(
|
214
|
-
f'{sys.executable} executable.py',
|
215
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
216
|
-
sandbox=sandbox,
|
217
|
-
artifacts=artifacts,
|
218
|
-
dependency_cache=dependency_cache,
|
219
|
-
)
|
220
|
-
return artifacts
|
221
|
-
|
222
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
223
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
224
|
-
assert artifacts.logs is not None
|
225
|
-
assert not artifacts.logs.cached
|
226
|
-
|
227
|
-
another_artifacts = await configure_and_run_with_dest(
|
228
|
-
pathlib.Path('another-out.txt')
|
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'))
|
345
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
346
|
-
assert another_artifacts.logs is not None
|
347
|
-
assert another_artifacts.logs.cached
|
348
|
-
|
349
|
-
|
350
|
-
async def test_run_fails_when_storage_value_is_changed_and_integrity_check_is_enabled(
|
351
|
-
cleandir: pathlib.Path,
|
352
|
-
dependency_cache: DependencyCache,
|
353
|
-
sandbox: SandboxBase,
|
354
|
-
file_cacher: FileCacher,
|
355
|
-
):
|
356
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
357
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
358
|
-
artifacts = GradingArtifacts()
|
359
|
-
artifacts.inputs.append(
|
360
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
361
|
-
)
|
362
|
-
artifacts.outputs.append(
|
363
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
|
364
|
-
)
|
365
|
-
await steps_with_caching.run(
|
366
|
-
f'{sys.executable} executable.py',
|
367
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
368
|
-
sandbox=sandbox,
|
369
|
-
artifacts=artifacts,
|
370
|
-
dependency_cache=dependency_cache,
|
371
|
-
)
|
372
|
-
return artifacts
|
373
|
-
|
374
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
375
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
376
|
-
assert artifacts.logs is not None
|
377
|
-
assert not artifacts.logs.cached
|
378
|
-
|
379
|
-
pathlib.Path('out.txt').write_text('42')
|
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
|
-
|
418
|
-
another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
419
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
420
|
-
assert another_artifacts.logs is not None
|
421
|
-
assert another_artifacts.logs.cached
|
422
|
-
|
423
|
-
|
424
|
-
async def test_run_evicts_when_storage_value_deleted(
|
425
|
-
cleandir: pathlib.Path,
|
426
|
-
dependency_cache: DependencyCache,
|
427
|
-
sandbox: SandboxBase,
|
428
|
-
file_cacher: FileCacher,
|
429
|
-
):
|
430
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
431
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
432
|
-
artifacts = GradingArtifacts()
|
433
|
-
artifacts.inputs.append(
|
434
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
435
|
-
)
|
436
|
-
artifacts.outputs.append(
|
437
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
|
438
|
-
)
|
439
|
-
await steps_with_caching.run(
|
440
|
-
f'{sys.executable} executable.py',
|
441
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
442
|
-
sandbox=sandbox,
|
443
|
-
artifacts=artifacts,
|
444
|
-
dependency_cache=dependency_cache,
|
445
|
-
)
|
446
|
-
return artifacts
|
447
|
-
|
448
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
449
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
450
|
-
assert artifacts.logs is not None
|
451
|
-
assert not artifacts.logs.cached
|
452
|
-
|
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)
|
457
|
-
|
458
|
-
another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
459
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
460
|
-
assert another_artifacts.logs is not None
|
461
|
-
assert not another_artifacts.logs.cached
|
462
|
-
|
463
|
-
|
464
|
-
async def test_run_overwrite_exec_bit_when_changed(
|
465
|
-
cleandir: pathlib.Path,
|
466
|
-
dependency_cache: DependencyCache,
|
467
|
-
sandbox: SandboxBase,
|
468
|
-
file_cacher: FileCacher,
|
469
|
-
):
|
470
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
471
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
472
|
-
artifacts = GradingArtifacts()
|
473
|
-
artifacts.inputs.append(
|
474
|
-
GradingFileInput(
|
475
|
-
**executable.expand(),
|
476
|
-
dest=pathlib.Path('executable.py'),
|
477
|
-
)
|
478
|
-
)
|
479
|
-
artifacts.outputs.append(
|
480
|
-
GradingFileOutput(
|
481
|
-
src=pathlib.Path('box-out.txt'), dest=dest, executable=True
|
482
|
-
)
|
483
|
-
)
|
484
|
-
await steps_with_caching.run(
|
485
|
-
f'{sys.executable} executable.py',
|
486
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
487
|
-
sandbox=sandbox,
|
488
|
-
artifacts=artifacts,
|
489
|
-
dependency_cache=dependency_cache,
|
490
|
-
)
|
491
|
-
return artifacts
|
492
|
-
|
493
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
494
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
495
|
-
assert artifacts.logs is not None
|
496
|
-
assert not artifacts.logs.cached
|
497
|
-
assert os.access('out.txt', os.X_OK)
|
498
|
-
|
499
|
-
pathlib.Path('out.txt').chmod(0o644)
|
500
|
-
|
501
|
-
another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
502
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
503
|
-
assert another_artifacts.logs is not None
|
504
|
-
assert another_artifacts.logs.cached
|
505
|
-
assert os.access('out.txt', os.X_OK)
|
506
|
-
|
507
|
-
|
508
|
-
async def test_run_evicts_when_changed_file_and_no_hash(
|
509
|
-
cleandir: pathlib.Path,
|
510
|
-
dependency_cache: DependencyCache,
|
511
|
-
sandbox: SandboxBase,
|
512
|
-
file_cacher: FileCacher,
|
513
|
-
):
|
514
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
515
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
516
|
-
artifacts = GradingArtifacts()
|
517
|
-
artifacts.inputs.append(
|
518
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
519
|
-
)
|
520
|
-
artifacts.outputs.append(
|
521
|
-
GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest, hash=False)
|
522
|
-
)
|
523
|
-
await steps_with_caching.run(
|
524
|
-
f'{sys.executable} executable.py',
|
525
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
526
|
-
sandbox=sandbox,
|
527
|
-
artifacts=artifacts,
|
528
|
-
dependency_cache=dependency_cache,
|
529
|
-
)
|
530
|
-
return artifacts
|
531
|
-
|
532
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
533
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
534
|
-
assert artifacts.logs is not None
|
535
|
-
assert not artifacts.logs.cached
|
536
|
-
|
537
|
-
pathlib.Path('out.txt').write_text('42')
|
538
|
-
|
539
|
-
another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
540
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
541
|
-
assert another_artifacts.logs is not None
|
542
|
-
assert not another_artifacts.logs.cached
|
543
|
-
|
544
|
-
|
545
|
-
async def test_run_evicts_when_exec_bit_different_and_no_hash(
|
546
|
-
cleandir: pathlib.Path,
|
547
|
-
dependency_cache: DependencyCache,
|
548
|
-
sandbox: SandboxBase,
|
549
|
-
file_cacher: FileCacher,
|
550
|
-
):
|
551
|
-
async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
|
552
|
-
executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
|
553
|
-
artifacts = GradingArtifacts()
|
554
|
-
artifacts.inputs.append(
|
555
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
556
|
-
)
|
557
|
-
artifacts.outputs.append(
|
558
|
-
GradingFileOutput(
|
559
|
-
src=pathlib.Path('box-out.txt'), dest=dest, hash=False, executable=True
|
560
|
-
)
|
561
|
-
)
|
562
|
-
await steps_with_caching.run(
|
563
|
-
f'{sys.executable} executable.py',
|
564
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
565
|
-
sandbox=sandbox,
|
566
|
-
artifacts=artifacts,
|
567
|
-
dependency_cache=dependency_cache,
|
568
|
-
)
|
569
|
-
return artifacts
|
570
|
-
|
571
|
-
artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
572
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
573
|
-
assert artifacts.logs is not None
|
574
|
-
assert not artifacts.logs.cached
|
575
|
-
|
576
|
-
pathlib.Path('out.txt').chmod(0o0644)
|
577
|
-
|
578
|
-
another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
|
579
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
580
|
-
assert another_artifacts.logs is not None
|
581
|
-
assert not another_artifacts.logs.cached
|
582
|
-
|
583
|
-
|
584
|
-
async def test_run_evicts_when_input_fingerprint_changes(
|
585
|
-
cleandir: pathlib.Path,
|
586
|
-
dependency_cache: DependencyCache,
|
587
|
-
sandbox: SandboxBase,
|
588
|
-
):
|
589
|
-
async def configure_and_run() -> GradingArtifacts:
|
590
|
-
executable = DigestOrSource.create(pathlib.Path('executable.py'))
|
591
|
-
artifacts = GradingArtifacts()
|
592
|
-
artifacts.inputs.append(
|
593
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
594
|
-
)
|
595
|
-
artifacts.outputs.append(
|
596
|
-
GradingFileOutput(
|
597
|
-
src=pathlib.Path('box-out.txt'),
|
598
|
-
dest=pathlib.Path('out.txt'),
|
599
|
-
)
|
600
|
-
)
|
601
|
-
await steps_with_caching.run(
|
602
|
-
f'{sys.executable} executable.py',
|
603
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
604
|
-
sandbox=sandbox,
|
605
|
-
artifacts=artifacts,
|
606
|
-
dependency_cache=dependency_cache,
|
607
|
-
)
|
608
|
-
return artifacts
|
609
|
-
|
610
|
-
pathlib.Path('executable.py').write_text('print(5)')
|
611
|
-
|
612
|
-
artifacts = await configure_and_run()
|
613
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
614
|
-
assert artifacts.logs is not None
|
615
|
-
assert not artifacts.logs.cached
|
616
|
-
|
617
|
-
pathlib.Path('executable.py').write_text('print(42)')
|
618
|
-
|
619
|
-
another_artifacts = await configure_and_run()
|
620
|
-
assert (cleandir / 'out.txt').read_text().strip() == '42'
|
621
|
-
assert another_artifacts.logs is not None
|
622
|
-
assert not another_artifacts.logs.cached
|
623
|
-
|
624
|
-
|
625
|
-
async def test_run_evicts_when_output_is_deleted_and_no_hash(
|
626
|
-
cleandir: pathlib.Path,
|
627
|
-
dependency_cache: DependencyCache,
|
628
|
-
sandbox: SandboxBase,
|
629
|
-
):
|
630
|
-
async def configure_and_run() -> GradingArtifacts:
|
631
|
-
executable = DigestOrSource.create(pathlib.Path('executable.py'))
|
632
|
-
artifacts = GradingArtifacts()
|
633
|
-
artifacts.inputs.append(
|
634
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
635
|
-
)
|
636
|
-
artifacts.outputs.append(
|
637
|
-
GradingFileOutput(
|
638
|
-
src=pathlib.Path('box-out.txt'),
|
639
|
-
dest=pathlib.Path('out.txt'),
|
640
|
-
hash=False,
|
641
|
-
)
|
642
|
-
)
|
643
|
-
await steps_with_caching.run(
|
644
|
-
f'{sys.executable} executable.py',
|
645
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
646
|
-
sandbox=sandbox,
|
647
|
-
artifacts=artifacts,
|
648
|
-
dependency_cache=dependency_cache,
|
649
|
-
)
|
650
|
-
return artifacts
|
651
|
-
|
652
|
-
pathlib.Path('executable.py').write_text('print(5)')
|
653
|
-
|
654
|
-
artifacts = await configure_and_run()
|
655
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
656
|
-
assert artifacts.logs is not None
|
657
|
-
assert not artifacts.logs.cached
|
658
|
-
|
659
|
-
pathlib.Path('out.txt').unlink()
|
660
|
-
|
661
|
-
another_artifacts = await configure_and_run()
|
662
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
663
|
-
assert another_artifacts.logs is not None
|
664
|
-
assert not another_artifacts.logs.cached
|
665
|
-
|
666
|
-
|
667
|
-
async def test_run_misses_when_input_file_changes(
|
668
|
-
cleandir: pathlib.Path,
|
669
|
-
dependency_cache: DependencyCache,
|
670
|
-
sandbox: SandboxBase,
|
671
|
-
file_cacher: FileCacher,
|
672
|
-
):
|
673
|
-
async def configure_and_run(number: int) -> GradingArtifacts:
|
674
|
-
executable = DigestOrSource.create(
|
675
|
-
file_cacher.put_file_text(f'print({number})')
|
676
|
-
)
|
677
|
-
artifacts = GradingArtifacts()
|
678
|
-
artifacts.inputs.append(
|
679
|
-
GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
|
680
|
-
)
|
681
|
-
artifacts.outputs.append(
|
682
|
-
GradingFileOutput(
|
683
|
-
src=pathlib.Path('box-out.txt'),
|
684
|
-
dest=pathlib.Path('out.txt'),
|
685
|
-
hash=False,
|
686
|
-
)
|
687
|
-
)
|
688
|
-
await steps_with_caching.run(
|
689
|
-
f'{sys.executable} executable.py',
|
690
|
-
params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
|
691
|
-
sandbox=sandbox,
|
692
|
-
artifacts=artifacts,
|
693
|
-
dependency_cache=dependency_cache,
|
694
|
-
)
|
695
|
-
return artifacts
|
696
|
-
|
697
|
-
artifacts = await configure_and_run(5)
|
698
|
-
assert (cleandir / 'out.txt').read_text().strip() == '5'
|
699
|
-
assert artifacts.logs is not None
|
700
|
-
assert not artifacts.logs.cached
|
701
|
-
|
702
|
-
pathlib.Path('out.txt').write_text('42')
|
703
|
-
|
704
|
-
another_artifacts = await configure_and_run(42)
|
705
|
-
assert (cleandir / 'out.txt').read_text().strip() == '42'
|
706
|
-
assert another_artifacts.logs is not None
|
707
|
-
assert not another_artifacts.logs.cached
|