rbx.cp 0.13.4__py3-none-any.whl → 0.13.5__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/checkers.py +2 -9
- rbx/box/cli.py +0 -1
- rbx/box/code.py +27 -80
- rbx/box/environment.py +16 -6
- rbx/box/generators.py +26 -3
- rbx/box/global_package.py +1 -1
- rbx/box/header.py +13 -1
- rbx/box/package.py +0 -14
- rbx/box/setter_config.py +11 -0
- rbx/box/solutions.py +12 -4
- rbx/box/tasks.py +9 -4
- rbx/box/testing/testing_package.py +7 -2
- rbx/box/ui/screens/run_explorer.py +0 -8
- rbx/box/ui/utils/run_ui.py +7 -3
- rbx/box/ui/widgets/test_output_box.py +1 -1
- rbx/box/validators.py +3 -1
- rbx/grading/caching.py +64 -14
- rbx/grading/judge/program.py +268 -0
- rbx/grading/judge/sandbox.py +30 -193
- rbx/grading/judge/sandboxes/stupid_sandbox.py +232 -241
- rbx/grading/judge/sandboxes/tee.py +31 -0
- rbx/grading/steps.py +87 -199
- rbx/grading/steps_with_caching.py +15 -6
- rbx/resources/presets/default/problem/problem.rbx.yml +0 -2
- rbx/resources/templates/rbx.h +43 -2
- rbx/testing_utils.py +7 -0
- rbx/utils.py +51 -1
- {rbx_cp-0.13.4.dist-info → rbx_cp-0.13.5.dist-info}/METADATA +1 -1
- {rbx_cp-0.13.4.dist-info → rbx_cp-0.13.5.dist-info}/RECORD +32 -34
- rbx/grading/judge/sandboxes/isolate.py +0 -695
- rbx/grading/judge/testiso.py +0 -54
- rbx/resources/envs/isolate.rbx.yml +0 -36
- rbx/resources/presets/default/problem/sols/slow.cpp +0 -15
- {rbx_cp-0.13.4.dist-info → rbx_cp-0.13.5.dist-info}/LICENSE +0 -0
- {rbx_cp-0.13.4.dist-info → rbx_cp-0.13.5.dist-info}/WHEEL +0 -0
- {rbx_cp-0.13.4.dist-info → rbx_cp-0.13.5.dist-info}/entry_points.txt +0 -0
rbx/grading/steps.py
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
import asyncio
|
2
|
-
import contextlib
|
3
2
|
import dataclasses
|
4
3
|
import functools
|
5
|
-
import os
|
6
4
|
import pathlib
|
7
5
|
import re
|
8
6
|
import shlex
|
9
7
|
import shutil
|
10
8
|
import subprocess
|
11
9
|
import sys
|
12
|
-
import tempfile
|
13
10
|
from enum import Enum
|
14
11
|
from typing import IO, Any, Dict, Iterable, List, Optional, Tuple, Union
|
15
12
|
|
@@ -20,9 +17,9 @@ from rich.text import Text
|
|
20
17
|
from rbx import utils
|
21
18
|
from rbx.config import get_bits_stdcpp, get_jngen, get_testlib
|
22
19
|
from rbx.console import console
|
23
|
-
from rbx.grading import grading_context
|
20
|
+
from rbx.grading import grading_context
|
24
21
|
from rbx.grading.judge.cacher import FileCacher
|
25
|
-
from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
|
22
|
+
from rbx.grading.judge.sandbox import SandboxBase, SandboxLog, SandboxParams
|
26
23
|
from rbx.grading.judge.storage import copyfileobj
|
27
24
|
from rbx.grading.limits import Limits
|
28
25
|
|
@@ -81,6 +78,7 @@ class DigestHolder(BaseModel):
|
|
81
78
|
|
82
79
|
class GradingLogsHolder(BaseModel):
|
83
80
|
run: Optional['RunLog'] = None
|
81
|
+
interactor_run: Optional['RunLog'] = None
|
84
82
|
preprocess: Optional[List['PreprocessLog']] = None
|
85
83
|
cached: bool = False
|
86
84
|
|
@@ -238,6 +236,7 @@ class RunLog(BaseModel):
|
|
238
236
|
sandbox: str = ''
|
239
237
|
warnings: bool = False
|
240
238
|
metadata: Optional[RunLogMetadata] = None
|
239
|
+
exitindex: int = 0
|
241
240
|
|
242
241
|
def get_run_language(self) -> Optional[str]:
|
243
242
|
if self.metadata is None:
|
@@ -394,16 +393,18 @@ def _expand_part(part: str, sandbox: SandboxBase) -> List[str]:
|
|
394
393
|
return [part]
|
395
394
|
|
396
395
|
|
397
|
-
def _get_java_memory_limits(
|
398
|
-
max_memory =
|
396
|
+
def _get_java_memory_limits(params: SandboxParams) -> Tuple[int, int]:
|
397
|
+
max_memory = params.address_space
|
399
398
|
if max_memory is None:
|
400
399
|
max_memory = 2048
|
401
400
|
return max_memory, min(512, int(max_memory * 0.9))
|
402
401
|
|
403
402
|
|
404
|
-
def _split_and_expand(
|
403
|
+
def _split_and_expand(
|
404
|
+
command: str, sandbox: SandboxBase, params: SandboxParams
|
405
|
+
) -> List[str]:
|
405
406
|
res = []
|
406
|
-
max_mem, init_mem = _get_java_memory_limits(
|
407
|
+
max_mem, init_mem = _get_java_memory_limits(params)
|
407
408
|
parts = shlex.split(command.format(memory=max_mem, initialMemory=init_mem))
|
408
409
|
for part in parts:
|
409
410
|
res.extend(_expand_part(part, sandbox))
|
@@ -640,6 +641,36 @@ def _check_for_compilation_warnings(
|
|
640
641
|
)
|
641
642
|
|
642
643
|
|
644
|
+
def _build_run_log(
|
645
|
+
sandbox_log: SandboxLog,
|
646
|
+
sandbox: SandboxBase,
|
647
|
+
params: SandboxParams,
|
648
|
+
metadata: Optional[RunLogMetadata] = None,
|
649
|
+
) -> RunLog:
|
650
|
+
execution_time = sandbox_log.execution_time
|
651
|
+
if execution_time is not None and (
|
652
|
+
sandbox_log.exitstatus == SandboxBase.EXIT_TIMEOUT
|
653
|
+
or sandbox_log.exitstatus == SandboxBase.EXIT_TIMEOUT_WALL
|
654
|
+
):
|
655
|
+
execution_time = max(execution_time, (params.timeout or 0.0) / 1000)
|
656
|
+
|
657
|
+
run_log = RunLog(
|
658
|
+
exitcode=sandbox_log.exitcode,
|
659
|
+
exitstatus=sandbox_log.exitstatus,
|
660
|
+
time=execution_time,
|
661
|
+
memory=sandbox_log.memory_used,
|
662
|
+
metadata=metadata,
|
663
|
+
sandbox=sandbox_log.dump_other_logs(),
|
664
|
+
exitindex=sandbox_log.exit_index,
|
665
|
+
)
|
666
|
+
if metadata is not None and metadata.is_sanitized:
|
667
|
+
run_log.warnings = _check_for_sanitizer_warnings(
|
668
|
+
sandbox,
|
669
|
+
params.stderr_file,
|
670
|
+
)
|
671
|
+
return run_log
|
672
|
+
|
673
|
+
|
643
674
|
def compile(
|
644
675
|
commands: List[str],
|
645
676
|
params: SandboxParams,
|
@@ -656,29 +687,20 @@ def compile(
|
|
656
687
|
return True
|
657
688
|
|
658
689
|
logs: List[PreprocessLog] = []
|
659
|
-
|
660
|
-
params.model_copy(deep=True)
|
661
|
-
) # Copy to allow further modification.
|
690
|
+
params = params.model_copy(deep=True) # Copy to allow further modification.
|
662
691
|
|
663
692
|
for i, command in enumerate(commands):
|
664
693
|
_maybe_complain_about_sanitization(command)
|
665
|
-
cmd = _split_and_expand(command, sandbox)
|
694
|
+
cmd = _split_and_expand(command, sandbox, params)
|
666
695
|
stdout_file = pathlib.PosixPath(f'compile-{i}.stdout')
|
667
696
|
stderr_file = pathlib.PosixPath(f'compile-{i}.stderr')
|
668
|
-
|
697
|
+
params.set_stdall(stdout=stdout_file, stderr=stderr_file)
|
669
698
|
|
670
699
|
# Remove memory constraints for Java.
|
671
700
|
if is_java_like_command(get_exe_from_command(command)):
|
672
|
-
|
701
|
+
params.address_space = None
|
673
702
|
|
674
|
-
|
675
|
-
console.print(
|
676
|
-
'[error]Sandbox crashed while processing command:[/error]',
|
677
|
-
utils.highlight_json_obj(cmd),
|
678
|
-
'[error]and logs[/error]',
|
679
|
-
sandbox.debug_message(),
|
680
|
-
)
|
681
|
-
return False
|
703
|
+
sandbox_log = sandbox.run(cmd, params)
|
682
704
|
|
683
705
|
std_outputs = [
|
684
706
|
sandbox.get_file_to_string(stderr_file, maxlen=None)
|
@@ -691,13 +713,13 @@ def compile(
|
|
691
713
|
|
692
714
|
log = PreprocessLog(
|
693
715
|
cmd=cmd,
|
694
|
-
exitcode=
|
695
|
-
exitstatus=
|
696
|
-
time=
|
697
|
-
memory=
|
716
|
+
exitcode=sandbox_log.exitcode,
|
717
|
+
exitstatus=sandbox_log.exitstatus,
|
718
|
+
time=sandbox_log.execution_time,
|
719
|
+
memory=sandbox_log.memory_used,
|
698
720
|
warnings=_check_for_compilation_warnings(sandbox, stderr_file),
|
699
721
|
log='\n'.join(std_outputs),
|
700
|
-
sandbox=
|
722
|
+
sandbox=sandbox_log.dump_other_logs(),
|
701
723
|
)
|
702
724
|
logs.append(log)
|
703
725
|
|
@@ -730,48 +752,19 @@ async def run(
|
|
730
752
|
|
731
753
|
_process_input_artifacts(artifacts, sandbox)
|
732
754
|
_process_fifos(artifacts, sandbox)
|
733
|
-
cmd = _split_and_expand(command, sandbox)
|
734
|
-
|
735
|
-
params.model_copy(deep=True)
|
736
|
-
) # Copy to allow further modification.
|
755
|
+
cmd = _split_and_expand(command, sandbox, params)
|
756
|
+
params = params.model_copy(deep=True) # Copy to allow further modification.
|
737
757
|
|
738
758
|
# Remove memory constraints for Java.
|
739
759
|
if is_java_like_command(get_exe_from_command(command)):
|
740
|
-
|
760
|
+
params.address_space = None
|
741
761
|
|
742
|
-
|
743
|
-
if not await asyncio.to_thread(sandbox.execute_without_std, cmd):
|
744
|
-
console.print(
|
745
|
-
'[error]Sandbox crashed while processing command:[/error]',
|
746
|
-
utils.highlight_json_obj(cmd),
|
747
|
-
'[error]and logs[/error]',
|
748
|
-
sandbox.debug_message(),
|
749
|
-
)
|
750
|
-
return None
|
762
|
+
sandbox_log = await asyncio.to_thread(sandbox.run, cmd, params)
|
751
763
|
|
752
764
|
if not _process_output_artifacts(artifacts, sandbox):
|
753
765
|
return None
|
754
766
|
|
755
|
-
|
756
|
-
if execution_time is not None and (
|
757
|
-
sandbox.get_exit_status() == SandboxBase.EXIT_TIMEOUT
|
758
|
-
or sandbox.get_exit_status() == SandboxBase.EXIT_TIMEOUT_WALL
|
759
|
-
):
|
760
|
-
execution_time = max(execution_time, (params.timeout or 0.0) / 1000)
|
761
|
-
|
762
|
-
run_log = RunLog(
|
763
|
-
exitcode=sandbox.get_exit_code(),
|
764
|
-
exitstatus=sandbox.get_exit_status(),
|
765
|
-
time=sandbox.get_execution_time(),
|
766
|
-
memory=sandbox.get_memory_used(),
|
767
|
-
metadata=metadata,
|
768
|
-
sandbox=sandbox.get_detailed_logs(),
|
769
|
-
)
|
770
|
-
if metadata is not None and metadata.is_sanitized:
|
771
|
-
run_log.warnings = _check_for_sanitizer_warnings(
|
772
|
-
sandbox,
|
773
|
-
params.stderr_file,
|
774
|
-
)
|
767
|
+
run_log = _build_run_log(sandbox_log, sandbox, params, metadata)
|
775
768
|
if artifacts.logs is not None:
|
776
769
|
artifacts.logs.run = run_log.model_copy()
|
777
770
|
return run_log
|
@@ -781,45 +774,53 @@ async def run(
|
|
781
774
|
class CoordinatedRunParams:
|
782
775
|
command: str
|
783
776
|
params: SandboxParams
|
784
|
-
sandbox: SandboxBase
|
785
|
-
artifacts: GradingArtifacts
|
786
777
|
metadata: Optional[RunLogMetadata] = None
|
787
778
|
|
788
779
|
|
789
780
|
async def run_coordinated(
|
790
781
|
interactor: CoordinatedRunParams,
|
791
782
|
solution: CoordinatedRunParams,
|
783
|
+
artifacts: GradingArtifacts,
|
784
|
+
sandbox: SandboxBase,
|
785
|
+
merged_capture: Optional[pathlib.Path] = None,
|
792
786
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
793
|
-
|
794
|
-
return asyncio.create_task(
|
795
|
-
run(
|
796
|
-
params.command,
|
797
|
-
params.params,
|
798
|
-
params.sandbox,
|
799
|
-
params.artifacts,
|
800
|
-
params.metadata,
|
801
|
-
)
|
802
|
-
)
|
787
|
+
sandbox.reset()
|
803
788
|
|
804
|
-
|
805
|
-
|
806
|
-
solution.sandbox.params.pgid = await interactor.sandbox.get_pid()
|
807
|
-
solution_task = run_one(solution)
|
789
|
+
_process_input_artifacts(artifacts, sandbox)
|
790
|
+
_process_fifos(artifacts, sandbox)
|
808
791
|
|
809
|
-
|
792
|
+
interactor_cmd = _split_and_expand(interactor.command, sandbox, interactor.params)
|
793
|
+
solution_cmd = _split_and_expand(solution.command, sandbox, solution.params)
|
810
794
|
|
811
|
-
|
795
|
+
interactor_params = interactor.params.model_copy(deep=True)
|
796
|
+
solution_params = solution.params.model_copy(deep=True)
|
812
797
|
|
798
|
+
if is_java_like_command(get_exe_from_command(solution.command)):
|
799
|
+
solution_params.address_space = None
|
800
|
+
|
801
|
+
solution_sandbox_log, interactor_sandbox_log = sandbox.run_communication(
|
802
|
+
solution_cmd,
|
803
|
+
solution_params,
|
804
|
+
interactor_cmd,
|
805
|
+
interactor_params,
|
806
|
+
merged_capture,
|
807
|
+
)
|
813
808
|
|
814
|
-
|
815
|
-
|
809
|
+
if not _process_output_artifacts(artifacts, sandbox):
|
810
|
+
return None, None
|
816
811
|
|
812
|
+
solution_log = _build_run_log(
|
813
|
+
solution_sandbox_log, sandbox, solution.params, solution.metadata
|
814
|
+
)
|
815
|
+
interactor_log = _build_run_log(
|
816
|
+
interactor_sandbox_log, sandbox, interactor.params, interactor.metadata
|
817
|
+
)
|
817
818
|
|
818
|
-
|
819
|
-
|
820
|
-
|
819
|
+
if artifacts.logs is not None:
|
820
|
+
artifacts.logs.run = solution_log
|
821
|
+
artifacts.logs.interactor_run = interactor_log
|
821
822
|
|
822
|
-
return
|
823
|
+
return solution_log, interactor_log
|
823
824
|
|
824
825
|
|
825
826
|
def get_checker_sandbox_params() -> SandboxParams:
|
@@ -830,116 +831,3 @@ def get_checker_sandbox_params() -> SandboxParams:
|
|
830
831
|
params.add_mapped_directory(pathlib.Path('/usr'))
|
831
832
|
params.add_mapped_directory(pathlib.Path('/etc'))
|
832
833
|
return params
|
833
|
-
|
834
|
-
|
835
|
-
def _check(
|
836
|
-
sandbox: SandboxBase,
|
837
|
-
testcase: TestcaseIO,
|
838
|
-
output_path: pathlib.Path,
|
839
|
-
should_use_python_checker: bool = False,
|
840
|
-
) -> CheckerResult:
|
841
|
-
if testcase.output is None:
|
842
|
-
# No output to compare.
|
843
|
-
return CheckerResult(outcome=Outcome.ACCEPTED)
|
844
|
-
|
845
|
-
if should_use_python_checker:
|
846
|
-
# Use default wcmp checker.
|
847
|
-
expected = testcase.output.read_text()
|
848
|
-
output = output_path.read_text()
|
849
|
-
|
850
|
-
return CheckerResult(outcome=_wcmp_check(expected, output))
|
851
|
-
|
852
|
-
sandbox.params.set_stdall(
|
853
|
-
stdin=None,
|
854
|
-
stdout=pathlib.PosixPath('stdout.txt'),
|
855
|
-
stderr=pathlib.PosixPath('stderr.txt'),
|
856
|
-
)
|
857
|
-
|
858
|
-
sandbox.create_file_from_string(
|
859
|
-
pathlib.PosixPath('expected.txt'), testcase.output.read_text(), override=True
|
860
|
-
)
|
861
|
-
sandbox.create_file_from_string(
|
862
|
-
pathlib.PosixPath('output.txt'), output_path.read_text(), override=True
|
863
|
-
)
|
864
|
-
sandbox.create_file_from_string(
|
865
|
-
pathlib.PosixPath('input.txt'),
|
866
|
-
testcase.input.read_text() if testcase.input is not None else '',
|
867
|
-
override=True,
|
868
|
-
)
|
869
|
-
|
870
|
-
if not sandbox.execute_without_std(
|
871
|
-
['./checker', 'input.txt', 'output.txt', 'expected.txt'],
|
872
|
-
):
|
873
|
-
console.print(
|
874
|
-
'[error]Sandbox crashed while running checker.[/error]',
|
875
|
-
'[error]and logs[/error]',
|
876
|
-
sandbox.debug_message(),
|
877
|
-
)
|
878
|
-
return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
|
879
|
-
|
880
|
-
checker_stderr = sandbox.get_file_to_string(
|
881
|
-
pathlib.PosixPath('stderr.txt'), maxlen=None
|
882
|
-
)
|
883
|
-
if sandbox.get_exit_code() in [1, 2]:
|
884
|
-
return CheckerResult(outcome=Outcome.WRONG_ANSWER, message=checker_stderr)
|
885
|
-
if sandbox.get_exit_code() == 3:
|
886
|
-
return CheckerResult(outcome=Outcome.JUDGE_FAILED, message=checker_stderr)
|
887
|
-
return CheckerResult(outcome=Outcome.ACCEPTED, message=checker_stderr)
|
888
|
-
|
889
|
-
|
890
|
-
# Always assume a `checker` executable in the sandbox if should use checker.
|
891
|
-
def evaluate(
|
892
|
-
sandbox: SandboxBase,
|
893
|
-
testcase: TestcaseIO,
|
894
|
-
log: Optional[TestcaseLog],
|
895
|
-
artifacts: GradingArtifacts,
|
896
|
-
should_use_python_checker: bool = False,
|
897
|
-
) -> Evaluation:
|
898
|
-
if log is None:
|
899
|
-
return Evaluation(
|
900
|
-
testcase=testcase,
|
901
|
-
log=TestcaseLog(),
|
902
|
-
result=CheckerResult(outcome=Outcome.INTERNAL_ERROR),
|
903
|
-
)
|
904
|
-
if log.exitstatus != SandboxBase.EXIT_OK:
|
905
|
-
return Evaluation(
|
906
|
-
testcase=testcase,
|
907
|
-
log=log,
|
908
|
-
result=CheckerResult(outcome=Outcome.RUNTIME_ERROR),
|
909
|
-
)
|
910
|
-
|
911
|
-
if not testcase.output:
|
912
|
-
# No output to compare.
|
913
|
-
return Evaluation(
|
914
|
-
testcase=testcase, log=log, result=CheckerResult(outcome=Outcome.ACCEPTED)
|
915
|
-
)
|
916
|
-
|
917
|
-
_process_input_artifacts(artifacts, sandbox)
|
918
|
-
if log.stdout_absolute_path is None:
|
919
|
-
return Evaluation(
|
920
|
-
testcase=testcase,
|
921
|
-
log=log,
|
922
|
-
result=CheckerResult(outcome=Outcome.INTERNAL_ERROR, message='No output'),
|
923
|
-
)
|
924
|
-
|
925
|
-
checker_result = _check(
|
926
|
-
sandbox,
|
927
|
-
testcase,
|
928
|
-
log.stdout_absolute_path,
|
929
|
-
should_use_python_checker=should_use_python_checker,
|
930
|
-
)
|
931
|
-
return Evaluation(
|
932
|
-
testcase=testcase,
|
933
|
-
log=log,
|
934
|
-
result=checker_result,
|
935
|
-
)
|
936
|
-
|
937
|
-
|
938
|
-
@contextlib.contextmanager
|
939
|
-
def make_fifos():
|
940
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
941
|
-
fifo_in = pathlib.PosixPath(temp_dir) / 'fifo.in'
|
942
|
-
fifo_out = pathlib.PosixPath(temp_dir) / 'fifo.out'
|
943
|
-
os.mkfifo(fifo_in)
|
944
|
-
os.mkfifo(fifo_out)
|
945
|
-
yield fifo_in, fifo_out
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import pathlib
|
1
2
|
from typing import Any, Dict, List, Optional, Tuple
|
2
3
|
|
3
4
|
from rbx.grading import grading_context, profiling, steps
|
@@ -88,10 +89,12 @@ async def run(
|
|
88
89
|
async def run_coordinated(
|
89
90
|
interactor: steps.CoordinatedRunParams,
|
90
91
|
solution: steps.CoordinatedRunParams,
|
92
|
+
artifacts: GradingArtifacts,
|
93
|
+
sandbox: SandboxBase,
|
91
94
|
dependency_cache: DependencyCache,
|
95
|
+
merged_capture: Optional[pathlib.Path] = None,
|
92
96
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
93
|
-
|
94
|
-
solution.artifacts.logs = GradingLogsHolder()
|
97
|
+
artifacts.logs = GradingLogsHolder()
|
95
98
|
|
96
99
|
cacheable_params = {
|
97
100
|
**_get_prefixed_cacheable_params(
|
@@ -114,18 +117,24 @@ async def run_coordinated(
|
|
114
117
|
cached_profile = profiling.Profiler('steps.run_coordinated[cached]', start=True)
|
115
118
|
with dependency_cache(
|
116
119
|
[interactor.command, solution.command],
|
117
|
-
[
|
120
|
+
[artifacts],
|
118
121
|
cacheable_params,
|
119
122
|
) as is_cached:
|
120
123
|
if not is_cached:
|
121
124
|
with profiling.Profiler('steps.run_coordinated'):
|
122
125
|
profiling.add_to_counter('steps.run_coordinated')
|
123
|
-
await steps.run_coordinated(
|
126
|
+
await steps.run_coordinated(
|
127
|
+
interactor,
|
128
|
+
solution,
|
129
|
+
artifacts,
|
130
|
+
sandbox,
|
131
|
+
merged_capture=merged_capture,
|
132
|
+
)
|
124
133
|
else:
|
125
134
|
cached_profile.stop()
|
126
135
|
profiling.add_to_counter('steps.run_coordinated[cached]')
|
127
136
|
|
128
137
|
return (
|
129
|
-
|
130
|
-
|
138
|
+
artifacts.logs.interactor_run,
|
139
|
+
artifacts.logs.run,
|
131
140
|
)
|
rbx/resources/templates/rbx.h
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
#ifndef _RBX_H
|
2
2
|
#define _RBX_H
|
3
|
+
#include <cstdint>
|
4
|
+
#include <limits>
|
3
5
|
#include <optional>
|
4
6
|
#include <stdexcept>
|
5
7
|
#include <string>
|
@@ -9,7 +11,7 @@ std::optional<std::string> getStringVar(std::string name) {
|
|
9
11
|
return std::nullopt;
|
10
12
|
}
|
11
13
|
|
12
|
-
std::optional<
|
14
|
+
std::optional<int64_t> getIntVar(std::string name) {
|
13
15
|
//<rbx::int_var>
|
14
16
|
return std::nullopt;
|
15
17
|
}
|
@@ -26,7 +28,46 @@ std::optional<bool> getBoolVar(std::string name) {
|
|
26
28
|
|
27
29
|
template <typename T> T getVar(std::string name);
|
28
30
|
|
29
|
-
template <>
|
31
|
+
template <> int32_t getVar<int32_t>(std::string name) {
|
32
|
+
auto opt = getIntVar(name);
|
33
|
+
if (!opt.has_value()) {
|
34
|
+
throw std::runtime_error("Variable " + name +
|
35
|
+
" is not an integer or could not be found");
|
36
|
+
}
|
37
|
+
if (opt.value() < std::numeric_limits<int32_t>::min() ||
|
38
|
+
opt.value() > std::numeric_limits<int32_t>::max()) {
|
39
|
+
throw std::runtime_error("Variable " + name + " of value " +
|
40
|
+
std::to_string(opt.value()) +
|
41
|
+
" does not fit in int32_t");
|
42
|
+
}
|
43
|
+
return opt.value();
|
44
|
+
}
|
45
|
+
|
46
|
+
template <> uint32_t getVar<uint32_t>(std::string name) {
|
47
|
+
auto opt = getIntVar(name);
|
48
|
+
if (!opt.has_value()) {
|
49
|
+
throw std::runtime_error("Variable " + name +
|
50
|
+
" is not an integer or could not be found");
|
51
|
+
}
|
52
|
+
if (opt.value() < std::numeric_limits<uint32_t>::min() ||
|
53
|
+
opt.value() > std::numeric_limits<uint32_t>::max()) {
|
54
|
+
throw std::runtime_error("Variable " + name + " of value " +
|
55
|
+
std::to_string(opt.value()) +
|
56
|
+
" does not fit in uint32_t");
|
57
|
+
}
|
58
|
+
return opt.value();
|
59
|
+
}
|
60
|
+
|
61
|
+
template <> int64_t getVar<int64_t>(std::string name) {
|
62
|
+
auto opt = getIntVar(name);
|
63
|
+
if (!opt.has_value()) {
|
64
|
+
throw std::runtime_error("Variable " + name +
|
65
|
+
" is not an integer or could not be found");
|
66
|
+
}
|
67
|
+
return opt.value();
|
68
|
+
}
|
69
|
+
|
70
|
+
template <> uint64_t getVar<uint64_t>(std::string name) {
|
30
71
|
auto opt = getIntVar(name);
|
31
72
|
if (!opt.has_value()) {
|
32
73
|
throw std::runtime_error("Variable " + name +
|
rbx/testing_utils.py
CHANGED
@@ -16,6 +16,13 @@ def get_testdata_path() -> pathlib.Path:
|
|
16
16
|
return file.parent
|
17
17
|
|
18
18
|
|
19
|
+
def get_resources_path() -> pathlib.Path:
|
20
|
+
with importlib.resources.as_file(
|
21
|
+
importlib.resources.files('rbx') / 'resources' / 'default_setter_config.yml'
|
22
|
+
) as file:
|
23
|
+
return file.parent
|
24
|
+
|
25
|
+
|
19
26
|
def clear_all_functools_cache():
|
20
27
|
from rbx.box import environment, header, package
|
21
28
|
|
rbx/utils.py
CHANGED
@@ -5,12 +5,15 @@ import json
|
|
5
5
|
import os
|
6
6
|
import os.path
|
7
7
|
import pathlib
|
8
|
+
import re
|
8
9
|
import resource
|
10
|
+
import shutil
|
9
11
|
import subprocess
|
10
12
|
import sys
|
11
|
-
from typing import Any, Optional, Type, TypeVar
|
13
|
+
from typing import Any, Optional, Type, TypeVar, Union
|
12
14
|
|
13
15
|
import rich
|
16
|
+
import rich.markup
|
14
17
|
import rich.prompt
|
15
18
|
import rich.status
|
16
19
|
import ruyaml
|
@@ -24,6 +27,7 @@ from rbx.console import console
|
|
24
27
|
|
25
28
|
T = TypeVar('T', bound=BaseModel)
|
26
29
|
APP_NAME = 'rbx'
|
30
|
+
PathOrStr = Union[pathlib.Path, str]
|
27
31
|
|
28
32
|
|
29
33
|
def create_and_write(path: pathlib.Path, *args, **kwargs):
|
@@ -37,6 +41,10 @@ def highlight_str(s: str) -> text.Text:
|
|
37
41
|
return txt
|
38
42
|
|
39
43
|
|
44
|
+
def escape_markup(s: str) -> str:
|
45
|
+
return rich.markup.escape(s, _escape=re.compile(r'(\\*)(\[)').sub)
|
46
|
+
|
47
|
+
|
40
48
|
def abspath(path: pathlib.Path) -> pathlib.Path:
|
41
49
|
return pathlib.Path(os.path.abspath(path))
|
42
50
|
|
@@ -185,6 +193,48 @@ def new_cd(x: pathlib.Path):
|
|
185
193
|
os.chdir(d)
|
186
194
|
|
187
195
|
|
196
|
+
def _safe_match(matcher, path):
|
197
|
+
try:
|
198
|
+
return matcher(path)
|
199
|
+
except ValueError:
|
200
|
+
return False
|
201
|
+
|
202
|
+
|
203
|
+
def copytree_honoring_gitignore(
|
204
|
+
src: pathlib.Path, dst: pathlib.Path, extra_gitignore: Optional[str] = None
|
205
|
+
):
|
206
|
+
from gitignore_parser import parse_gitignore, parse_gitignore_str
|
207
|
+
|
208
|
+
ignore_matchers = []
|
209
|
+
|
210
|
+
if extra_gitignore is not None:
|
211
|
+
ignore_matchers.append(parse_gitignore_str(extra_gitignore, base_dir=src))
|
212
|
+
|
213
|
+
for file in src.rglob('.gitignore'):
|
214
|
+
if file.is_file():
|
215
|
+
ignore_matchers.append(parse_gitignore(file))
|
216
|
+
|
217
|
+
# TODO: use recursive walk
|
218
|
+
for file in src.rglob('*'):
|
219
|
+
matching_file = file
|
220
|
+
ignored = False
|
221
|
+
while matching_file.is_relative_to(src):
|
222
|
+
if any(
|
223
|
+
_safe_match(ignore_matcher, matching_file)
|
224
|
+
for ignore_matcher in ignore_matchers
|
225
|
+
):
|
226
|
+
ignored = True
|
227
|
+
break
|
228
|
+
matching_file = matching_file.parent
|
229
|
+
if ignored:
|
230
|
+
continue
|
231
|
+
rel = relpath(file, src)
|
232
|
+
if file.is_file():
|
233
|
+
write_to = dst / rel
|
234
|
+
write_to.parent.mkdir(parents=True, exist_ok=True)
|
235
|
+
shutil.copyfile(file, write_to)
|
236
|
+
|
237
|
+
|
188
238
|
class StatusProgress(rich.status.Status):
|
189
239
|
_message: str
|
190
240
|
processed: int
|