rbx.cp 0.5.61__py3-none-any.whl → 0.5.63__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/cd.py +14 -0
- rbx/box/cli.py +6 -0
- rbx/box/code.py +34 -5
- rbx/box/contest/main.py +6 -2
- rbx/box/git_utils.py +28 -0
- rbx/box/package.py +23 -0
- rbx/box/packaging/boca/packager.py +3 -18
- rbx/box/packaging/moj/packager.py +1 -1
- rbx/box/packaging/polygon/upload.py +7 -5
- rbx/box/presets/__init__.py +80 -6
- rbx/box/presets/fetch.py +18 -1
- rbx/box/retries.py +2 -0
- rbx/box/solutions.py +242 -114
- rbx/box/solutions_test.py +3 -1
- rbx/box/tasks.py +6 -1
- rbx/box/testcase_utils.py +3 -0
- rbx/box/ui/css/app.tcss +14 -2
- rbx/box/ui/main.py +3 -5
- rbx/box/ui/screens/error.py +19 -0
- rbx/box/ui/screens/run.py +4 -12
- rbx/box/ui/screens/run_explorer.py +77 -1
- rbx/box/ui/screens/run_test_explorer.py +166 -0
- rbx/box/ui/screens/selector.py +26 -0
- rbx/box/ui/screens/test_explorer.py +33 -5
- rbx/box/ui/utils/__init__.py +0 -0
- rbx/box/ui/utils/run_ui.py +95 -0
- rbx/box/ui/widgets/__init__.py +0 -0
- rbx/box/ui/widgets/file_log.py +3 -1
- rbx/box/ui/widgets/interaction_box.py +59 -0
- rbx/box/ui/widgets/test_output_box.py +113 -0
- rbx/box/ui/widgets/two_sided_test_output_box.py +60 -0
- rbx/grading/steps.py +1 -0
- rbx/resources/packagers/boca/compile/java +55 -59
- rbx/resources/packagers/boca/interactive/java +2 -2
- rbx/resources/packagers/boca/run/java +2 -2
- {rbx_cp-0.5.61.dist-info → rbx_cp-0.5.63.dist-info}/METADATA +1 -1
- {rbx_cp-0.5.61.dist-info → rbx_cp-0.5.63.dist-info}/RECORD +40 -30
- {rbx_cp-0.5.61.dist-info → rbx_cp-0.5.63.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.61.dist-info → rbx_cp-0.5.63.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.61.dist-info → rbx_cp-0.5.63.dist-info}/entry_points.txt +0 -0
rbx/box/cd.py
CHANGED
@@ -25,6 +25,20 @@ def find_package(root: pathlib.Path = pathlib.Path()) -> Optional[pathlib.Path]:
|
|
25
25
|
return root
|
26
26
|
|
27
27
|
|
28
|
+
def is_problem_package(root: pathlib.Path = pathlib.Path()) -> bool:
|
29
|
+
dir = find_package(root)
|
30
|
+
if dir is None:
|
31
|
+
return False
|
32
|
+
return (dir / 'problem.rbx.yml').is_file()
|
33
|
+
|
34
|
+
|
35
|
+
def is_contest_package(root: pathlib.Path = pathlib.Path()) -> bool:
|
36
|
+
dir = find_package(root)
|
37
|
+
if dir is None:
|
38
|
+
return False
|
39
|
+
return (dir / 'contest.rbx.yml').is_file()
|
40
|
+
|
41
|
+
|
28
42
|
def within_closest_package(func):
|
29
43
|
@functools.wraps(func)
|
30
44
|
def wrapper(*args, **kwargs):
|
rbx/box/cli.py
CHANGED
@@ -112,6 +112,12 @@ def main(
|
|
112
112
|
help='Whether to save extra debug logs along with the evaluation results.',
|
113
113
|
),
|
114
114
|
):
|
115
|
+
if cd.is_problem_package() and not package.is_cache_valid():
|
116
|
+
console.console.print(
|
117
|
+
'[warning]Cache is incompatible with the current version of [item]rbx[/item], so it will be cleared.[/warning]'
|
118
|
+
)
|
119
|
+
clear()
|
120
|
+
|
115
121
|
state.STATE.run_through_cli = True
|
116
122
|
state.STATE.sanitized = sanitized
|
117
123
|
if sanitized:
|
rbx/box/code.py
CHANGED
@@ -155,6 +155,34 @@ def _ignore_warning_in_cxx_input(input: GradingFileInput):
|
|
155
155
|
input.src = preprocessed_path
|
156
156
|
|
157
157
|
|
158
|
+
def _maybe_rename_java_class(
|
159
|
+
compilable_path: pathlib.Path, file_mapping: FileMapping
|
160
|
+
) -> pathlib.Path:
|
161
|
+
mapped_path = PosixPath(file_mapping.compilable)
|
162
|
+
if mapped_path.suffix != '.java':
|
163
|
+
return compilable_path
|
164
|
+
import re
|
165
|
+
|
166
|
+
cls_name = mapped_path.stem
|
167
|
+
|
168
|
+
java_content = compilable_path.read_text()
|
169
|
+
regex = re.compile(r'public\s+class\s+[A-Za-z0-9_$]+([^A-Za-z0-9_$])')
|
170
|
+
match = regex.search(java_content)
|
171
|
+
if match is None:
|
172
|
+
console.console.print(
|
173
|
+
f'[error]Java public class not found in file: [item]{compilable_path}[/item][/error]'
|
174
|
+
)
|
175
|
+
raise typer.Exit(1)
|
176
|
+
|
177
|
+
new_content = regex.sub(f'public class {cls_name}\\1', java_content)
|
178
|
+
if new_content == java_content:
|
179
|
+
return compilable_path
|
180
|
+
|
181
|
+
preprocessed_path = package.get_problem_preprocessed_path(compilable_path)
|
182
|
+
preprocessed_path.write_text(new_content)
|
183
|
+
return preprocessed_path
|
184
|
+
|
185
|
+
|
158
186
|
def _format_stack_limit(limit: int) -> str:
|
159
187
|
if limit == resource.RLIM_INFINITY:
|
160
188
|
return 'unlimited'
|
@@ -372,11 +400,11 @@ def compile_item(
|
|
372
400
|
) -> str:
|
373
401
|
_check_stack_limit()
|
374
402
|
|
375
|
-
|
403
|
+
compilable_path = PosixPath(code.path)
|
376
404
|
|
377
|
-
if not
|
405
|
+
if not compilable_path.is_file():
|
378
406
|
console.console.print(
|
379
|
-
f'[error]Compilation file not found: [item]{
|
407
|
+
f'[error]Compilation file not found: [item]{compilable_path}[/item][/error]'
|
380
408
|
)
|
381
409
|
raise typer.Exit(1)
|
382
410
|
|
@@ -389,7 +417,7 @@ def compile_item(
|
|
389
417
|
|
390
418
|
if not compilation_options.commands:
|
391
419
|
# Language is not compiled.
|
392
|
-
return sandbox.file_cacher.put_file_from_path(
|
420
|
+
return sandbox.file_cacher.put_file_from_path(compilable_path)
|
393
421
|
|
394
422
|
commands = get_mapped_commands(compilation_options.commands, file_mapping)
|
395
423
|
commands = add_warning_flags(commands, force_warnings)
|
@@ -417,8 +445,9 @@ def compile_item(
|
|
417
445
|
download.maybe_add_testlib(code, artifacts)
|
418
446
|
download.maybe_add_jngen(code, artifacts)
|
419
447
|
download.maybe_add_rbx_header(code, artifacts)
|
448
|
+
compilable_path = _maybe_rename_java_class(compilable_path, file_mapping)
|
420
449
|
artifacts.inputs.append(
|
421
|
-
GradingFileInput(src=
|
450
|
+
GradingFileInput(src=compilable_path, dest=PosixPath(file_mapping.compilable))
|
422
451
|
)
|
423
452
|
|
424
453
|
artifacts.outputs.append(
|
rbx/box/contest/main.py
CHANGED
@@ -63,8 +63,10 @@ def create(
|
|
63
63
|
)
|
64
64
|
raise typer.Exit(1)
|
65
65
|
|
66
|
-
if fetch_info.
|
66
|
+
if fetch_info.is_remote():
|
67
67
|
preset = presets.install_from_remote(fetch_info)
|
68
|
+
elif fetch_info.is_local_dir():
|
69
|
+
preset = presets.install_from_local_dir(fetch_info)
|
68
70
|
|
69
71
|
preset_cfg = presets.get_installed_preset(preset)
|
70
72
|
preset_path = (
|
@@ -108,7 +110,9 @@ def create(
|
|
108
110
|
lock.unlink(missing_ok=True)
|
109
111
|
|
110
112
|
if local:
|
111
|
-
|
113
|
+
presets.copy_local_preset(
|
114
|
+
preset_path, dest_path, remote_uri=fetch_info.uri or preset_cfg.uri
|
115
|
+
)
|
112
116
|
|
113
117
|
with cd.new_package_cd(dest_path):
|
114
118
|
contest_utils.clear_all_caches()
|
rbx/box/git_utils.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import pathlib
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
import git
|
5
|
+
|
6
|
+
|
7
|
+
def get_repo_or_nil(
|
8
|
+
root: pathlib.Path = pathlib.Path(), search_parent_directories: bool = False
|
9
|
+
) -> Optional[git.Repo]:
|
10
|
+
try:
|
11
|
+
return git.Repo(root, search_parent_directories=search_parent_directories)
|
12
|
+
except git.InvalidGitRepositoryError:
|
13
|
+
return None
|
14
|
+
|
15
|
+
|
16
|
+
def is_repo(path: pathlib.Path) -> bool:
|
17
|
+
return get_repo_or_nil(path, search_parent_directories=False) is not None
|
18
|
+
|
19
|
+
|
20
|
+
def is_within_repo(path: pathlib.Path) -> bool:
|
21
|
+
return get_repo_or_nil(path, search_parent_directories=True) is not None
|
22
|
+
|
23
|
+
|
24
|
+
def get_any_remote(repo: git.Repo) -> Optional[git.Remote]:
|
25
|
+
for remote in repo.remotes:
|
26
|
+
if remote.exists():
|
27
|
+
return remote
|
28
|
+
return None
|
rbx/box/package.py
CHANGED
@@ -33,6 +33,7 @@ from rbx.grading.judge.storage import FilesystemStorage, Storage
|
|
33
33
|
YAML_NAME = 'problem.rbx.yml'
|
34
34
|
_DEFAULT_CHECKER = 'wcmp.cpp'
|
35
35
|
TEMP_DIR = None
|
36
|
+
CACHE_STEP_VERSION = 1
|
36
37
|
|
37
38
|
|
38
39
|
def warn_preset_deactivated(root: pathlib.Path = pathlib.Path()):
|
@@ -135,10 +136,17 @@ def get_ruyaml(root: pathlib.Path = pathlib.Path()) -> Tuple[ruyaml.YAML, ruyaml
|
|
135
136
|
return res, res.load(problem_yaml_path.read_text())
|
136
137
|
|
137
138
|
|
139
|
+
def _get_fingerprint() -> str:
|
140
|
+
return f'{CACHE_STEP_VERSION}'
|
141
|
+
|
142
|
+
|
138
143
|
@functools.cache
|
139
144
|
def get_problem_cache_dir(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
|
140
145
|
cache_dir = find_problem(root) / '.box'
|
141
146
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
147
|
+
fingerprint_file = cache_dir / 'fingerprint'
|
148
|
+
if not fingerprint_file.is_file():
|
149
|
+
fingerprint_file.write_text(_get_fingerprint())
|
142
150
|
return cache_dir
|
143
151
|
|
144
152
|
|
@@ -426,6 +434,21 @@ def get_merged_capture_path(root: pathlib.Path = pathlib.Path()) -> pathlib.Path
|
|
426
434
|
return path
|
427
435
|
|
428
436
|
|
437
|
+
@functools.cache
|
438
|
+
def is_cache_valid(root: pathlib.Path = pathlib.Path()):
|
439
|
+
cache_dir = find_problem(root) / '.box'
|
440
|
+
if not cache_dir.is_dir():
|
441
|
+
return
|
442
|
+
|
443
|
+
fingerprint_file = cache_dir / 'fingerprint'
|
444
|
+
if not fingerprint_file.is_file():
|
445
|
+
return False
|
446
|
+
fingerprint = fingerprint_file.read_text()
|
447
|
+
if fingerprint.strip() != _get_fingerprint():
|
448
|
+
return False
|
449
|
+
return True
|
450
|
+
|
451
|
+
|
429
452
|
def clear_package_cache():
|
430
453
|
pkgs = [sys.modules[__name__]]
|
431
454
|
|
@@ -67,7 +67,7 @@ class BocaPackager(BasePackager):
|
|
67
67
|
return (
|
68
68
|
f'basename={self._get_problem_basename()}\n'
|
69
69
|
f'fullname={statement.title}\n'
|
70
|
-
f'descfile={self.
|
70
|
+
f'descfile={self._get_problem_basename()}.pdf\n'
|
71
71
|
)
|
72
72
|
|
73
73
|
def _get_pkg_timelimit(self, language: BocaLanguage) -> int:
|
@@ -205,7 +205,7 @@ class BocaPackager(BasePackager):
|
|
205
205
|
compile_text = compile_text.replace('{{rbxFlags}}', flags[language])
|
206
206
|
return compile_text
|
207
207
|
|
208
|
-
def _copy_solutions(self, into_path: pathlib.Path
|
208
|
+
def _copy_solutions(self, into_path: pathlib.Path):
|
209
209
|
into_path = into_path / 'solutions'
|
210
210
|
for solution in package.get_solutions():
|
211
211
|
dest_path = (
|
@@ -218,21 +218,6 @@ class BocaPackager(BasePackager):
|
|
218
218
|
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
219
219
|
shutil.copy(str(solution.path), dest_path)
|
220
220
|
|
221
|
-
if solution.path.suffix == '.java':
|
222
|
-
java_content = dest_path.read_text()
|
223
|
-
if (
|
224
|
-
'class Main ' not in java_content
|
225
|
-
and f'class {self._get_problem_name()} ' not in java_content
|
226
|
-
):
|
227
|
-
console.console.print(
|
228
|
-
'[error]For BOCA packaging, Java solutions must be named `class Main` or `class <ProblemName>`.[/error]'
|
229
|
-
)
|
230
|
-
dest_path.write_text(
|
231
|
-
java_content.replace(
|
232
|
-
'class Main ', f'class {self._get_problem_name()} '
|
233
|
-
)
|
234
|
-
)
|
235
|
-
|
236
221
|
@classmethod
|
237
222
|
def name(cls) -> str:
|
238
223
|
return 'boca'
|
@@ -297,7 +282,7 @@ class BocaPackager(BasePackager):
|
|
297
282
|
(description_path / 'problem.info').write_text(self._get_problem_info())
|
298
283
|
shutil.copyfile(
|
299
284
|
self._get_main_built_statement(built_statements).path,
|
300
|
-
(description_path / self.
|
285
|
+
(description_path / self._get_problem_basename()).with_suffix('.pdf'),
|
301
286
|
)
|
302
287
|
|
303
288
|
# Copy solutions
|
@@ -307,11 +307,13 @@ def _upload_statement(problem: api.Problem):
|
|
307
307
|
polygon_statement = api.Statement(
|
308
308
|
encoding='utf-8',
|
309
309
|
name=statement.title,
|
310
|
-
legend=blocks.blocks.get('legend'),
|
311
|
-
input=blocks.blocks.get('input'),
|
312
|
-
output=blocks.blocks.get('output'),
|
313
|
-
interaction=blocks.blocks.get('interaction')
|
314
|
-
|
310
|
+
legend=blocks.blocks.get('legend') or '',
|
311
|
+
input=blocks.blocks.get('input') or '',
|
312
|
+
output=blocks.blocks.get('output') or '',
|
313
|
+
interaction=(blocks.blocks.get('interaction') or '')
|
314
|
+
if pkg.type == TaskType.COMMUNICATION
|
315
|
+
else None,
|
316
|
+
notes=_get_notes_with_explanations(blocks) or '',
|
315
317
|
)
|
316
318
|
problem.save_statement(
|
317
319
|
lang=code_to_langs([language])[0], problem_statement=polygon_statement
|
rbx/box/presets/__init__.py
CHANGED
@@ -3,13 +3,14 @@ import shutil
|
|
3
3
|
import tempfile
|
4
4
|
from typing import Annotated, Iterable, List, Optional, Sequence, Union
|
5
5
|
|
6
|
+
import questionary
|
6
7
|
import rich
|
7
8
|
import rich.prompt
|
8
9
|
import typer
|
9
10
|
from iso639.language import functools
|
10
11
|
|
11
12
|
from rbx import console, utils
|
12
|
-
from rbx.box import cd
|
13
|
+
from rbx.box import cd, git_utils
|
13
14
|
from rbx.box.environment import get_environment_path
|
14
15
|
from rbx.box.presets.fetch import PresetFetchInfo, get_preset_fetch_info
|
15
16
|
from rbx.box.presets.lock_schema import LockedAsset, PresetLock
|
@@ -363,11 +364,17 @@ def _install(root: pathlib.Path = pathlib.Path(), force: bool = False):
|
|
363
364
|
if not res:
|
364
365
|
raise typer.Exit(1)
|
365
366
|
shutil.rmtree(str(installation_path), ignore_errors=True)
|
366
|
-
|
367
|
+
copy_tree_normalizing_gitdir(root, installation_path)
|
367
368
|
shutil.rmtree(str(installation_path / 'build'), ignore_errors=True)
|
368
369
|
shutil.rmtree(str(installation_path / '.box'), ignore_errors=True)
|
369
370
|
shutil.rmtree(str(installation_path / '.local.rbx'), ignore_errors=True)
|
370
|
-
|
371
|
+
|
372
|
+
|
373
|
+
def install_from_local_dir(fetch_info: PresetFetchInfo, force: bool = False) -> str:
|
374
|
+
pd = pathlib.Path(fetch_info.inner_dir)
|
375
|
+
preset = get_preset_yaml(pd)
|
376
|
+
_install(pd, force=force)
|
377
|
+
return preset.name
|
371
378
|
|
372
379
|
|
373
380
|
def install_from_remote(fetch_info: PresetFetchInfo, force: bool = False) -> str:
|
@@ -452,12 +459,75 @@ def _sync(try_update: bool = False):
|
|
452
459
|
generate_lock(preset_lock.preset_name)
|
453
460
|
|
454
461
|
|
462
|
+
def copy_tree_normalizing_gitdir(src_path: pathlib.Path, dst_path: pathlib.Path):
|
463
|
+
shutil.copytree(str(src_path), str(dst_path))
|
464
|
+
if not (src_path / '.git').is_file():
|
465
|
+
return
|
466
|
+
|
467
|
+
src_repo = git_utils.get_repo_or_nil(src_path)
|
468
|
+
if src_repo is None:
|
469
|
+
return
|
470
|
+
|
471
|
+
gitdir_dst = dst_path / '.git'
|
472
|
+
shutil.rmtree(str(gitdir_dst), ignore_errors=True)
|
473
|
+
gitdir_dst.unlink(missing_ok=True)
|
474
|
+
|
475
|
+
shutil.copytree(str(src_repo.git_dir), str(gitdir_dst))
|
476
|
+
|
477
|
+
|
478
|
+
def copy_local_preset(
|
479
|
+
preset_path: pathlib.Path, dest_path: pathlib.Path, remote_uri: Optional[str] = None
|
480
|
+
):
|
481
|
+
copy_tree_normalizing_gitdir(preset_path, dest_path / '.local.rbx')
|
482
|
+
|
483
|
+
from rbx.box import git_utils
|
484
|
+
|
485
|
+
preset_repo = git_utils.get_repo_or_nil(preset_path)
|
486
|
+
current_repo = git_utils.get_repo_or_nil(
|
487
|
+
pathlib.Path.cwd(), search_parent_directories=True
|
488
|
+
)
|
489
|
+
|
490
|
+
if preset_repo is None or current_repo is None:
|
491
|
+
return
|
492
|
+
|
493
|
+
fetch_info = get_preset_fetch_info(remote_uri)
|
494
|
+
remote_uri = fetch_info.fetch_uri if fetch_info is not None else None
|
495
|
+
|
496
|
+
preset_remote = git_utils.get_any_remote(preset_repo)
|
497
|
+
preset_remote_uri = preset_remote.url if preset_remote is not None else remote_uri
|
498
|
+
if preset_remote_uri is None:
|
499
|
+
return
|
500
|
+
|
501
|
+
add_submodule = questionary.confirm(
|
502
|
+
'The preset is installed from a remote Git repository. Do you want to add it as a submodule of your project?',
|
503
|
+
default=False,
|
504
|
+
).ask()
|
505
|
+
if not add_submodule:
|
506
|
+
return
|
507
|
+
|
508
|
+
dest_path_rel = dest_path.resolve().relative_to(pathlib.Path.cwd().resolve())
|
509
|
+
path_str = str(dest_path_rel / '.local.rbx')
|
510
|
+
try:
|
511
|
+
current_repo.git.submodule('add', preset_remote_uri, path_str)
|
512
|
+
except Exception as e:
|
513
|
+
console.console.print('[error]Failed to add preset as a submodule.[/error]')
|
514
|
+
console.console.print(f'[error]Error:[/error] {e}')
|
515
|
+
console.console.print(
|
516
|
+
'[error]You might want to do this manually with the [item]git submodule add[/item] command.[/error]'
|
517
|
+
)
|
518
|
+
raise typer.Exit(1) from None
|
519
|
+
console.console.print(
|
520
|
+
f'[success]Preset [item]{preset_remote_uri}[/item] was added as a submodule to your project at [item]{path_str}[/item].[/success]'
|
521
|
+
)
|
522
|
+
|
523
|
+
|
455
524
|
@app.command(
|
456
525
|
'install', help='Install preset from current directory or from the given URI.'
|
457
526
|
)
|
458
527
|
def install(
|
459
528
|
uri: Optional[str] = typer.Argument(
|
460
|
-
None,
|
529
|
+
None,
|
530
|
+
help='URI for the preset to install. Might be a Github repository, or even a local path.',
|
461
531
|
),
|
462
532
|
):
|
463
533
|
if uri is None:
|
@@ -468,9 +538,13 @@ def install(
|
|
468
538
|
if fetch_info is None:
|
469
539
|
console.console.print(f'[error] Preset with URI {uri} not found.[/error]')
|
470
540
|
raise typer.Exit(1)
|
471
|
-
if fetch_info.
|
541
|
+
if not fetch_info.is_local_dir() and not fetch_info.is_remote():
|
472
542
|
console.console.print(f'[error]URI {uri} is invalid.[/error]')
|
473
|
-
|
543
|
+
raise typer.Exit(1)
|
544
|
+
if fetch_info.is_remote():
|
545
|
+
install_from_remote(fetch_info)
|
546
|
+
else:
|
547
|
+
install_from_local_dir(fetch_info)
|
474
548
|
|
475
549
|
|
476
550
|
@app.command('update', help='Update installed remote presets')
|
rbx/box/presets/fetch.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import pathlib
|
1
2
|
import re
|
2
3
|
from typing import Optional
|
3
4
|
|
@@ -17,6 +18,12 @@ class PresetFetchInfo(BaseModel):
|
|
17
18
|
# Inner directory from where to pull the preset.
|
18
19
|
inner_dir: str = ''
|
19
20
|
|
21
|
+
def is_remote(self) -> bool:
|
22
|
+
return self.fetch_uri is not None
|
23
|
+
|
24
|
+
def is_local_dir(self) -> bool:
|
25
|
+
return bool(self.inner_dir) and not self.is_remote()
|
26
|
+
|
20
27
|
|
21
28
|
def get_preset_fetch_info(uri: Optional[str]) -> Optional[PresetFetchInfo]:
|
22
29
|
if uri is None:
|
@@ -36,7 +43,7 @@ def get_preset_fetch_info(uri: Optional[str]) -> Optional[PresetFetchInfo]:
|
|
36
43
|
)
|
37
44
|
|
38
45
|
def get_short_github_fetch_info(s: str) -> Optional[PresetFetchInfo]:
|
39
|
-
pattern = r'([\w\-]+\/[\w\.\-]+)(?:\/(.*))?'
|
46
|
+
pattern = r'(?:\@gh/)?([\w\-]+\/[\w\.\-]+)(?:\/(.*))?'
|
40
47
|
compiled = re.compile(pattern)
|
41
48
|
match = compiled.match(s)
|
42
49
|
if match is None:
|
@@ -48,6 +55,15 @@ def get_preset_fetch_info(uri: Optional[str]) -> Optional[PresetFetchInfo]:
|
|
48
55
|
inner_dir=match.group(2) or '',
|
49
56
|
)
|
50
57
|
|
58
|
+
def get_local_dir_fetch_info(s: str) -> Optional[PresetFetchInfo]:
|
59
|
+
try:
|
60
|
+
path = pathlib.Path(s)
|
61
|
+
if not path.exists():
|
62
|
+
return None
|
63
|
+
except Exception:
|
64
|
+
return None
|
65
|
+
return PresetFetchInfo(name=path.name, inner_dir=str(path))
|
66
|
+
|
51
67
|
def get_local_fetch_info(s: str) -> Optional[PresetFetchInfo]:
|
52
68
|
pattern = r'[\w\-]+'
|
53
69
|
compiled = re.compile(pattern)
|
@@ -59,6 +75,7 @@ def get_preset_fetch_info(uri: Optional[str]) -> Optional[PresetFetchInfo]:
|
|
59
75
|
extractors = [
|
60
76
|
get_github_fetch_info,
|
61
77
|
get_short_github_fetch_info,
|
78
|
+
get_local_dir_fetch_info,
|
62
79
|
get_local_fetch_info,
|
63
80
|
]
|
64
81
|
|
rbx/box/retries.py
CHANGED
@@ -88,6 +88,8 @@ def _move_logs_to_temp_dir(
|
|
88
88
|
recover.append(_move_to_temp_dir(eval.log.stderr_absolute_path, temp_dir))
|
89
89
|
if eval.log.log_absolute_path is not None and eval.log.log_absolute_path.exists():
|
90
90
|
recover.append(_move_to_temp_dir(eval.log.log_absolute_path, temp_dir))
|
91
|
+
if eval.log.eval_absolute_path is not None and eval.log.eval_absolute_path.exists():
|
92
|
+
recover.append(_move_to_temp_dir(eval.log.eval_absolute_path, temp_dir))
|
91
93
|
return recover
|
92
94
|
|
93
95
|
|