diff --git a/pisek/opendata/managers.py b/pisek/opendata/managers.py index ec6d7bc9..90256342 100644 --- a/pisek/opendata/managers.py +++ b/pisek/opendata/managers.py @@ -1,7 +1,7 @@ from pisek.jobs.jobs import Job from pisek.jobs.status import StatusJobManager from pisek.jobs.job_pipeline import JobPipeline -from pisek.utils.paths import InputPath, OutputPath, RawPath +from pisek.utils.paths import IInputPath, IOutputPath, IRawPath from pisek.task_jobs.tools import sanitize_job, sanitize_job_direct from pisek.task_jobs.data.testcase_info import TestcaseInfo, TestcaseGenerationMode @@ -17,12 +17,12 @@ def __init__( gen_input: bool, gen_output: bool, check: bool, - input_: InputPath, + input_: IInputPath, info: TestcaseInfo, test: int, seed: int | None, - correct_output: OutputPath, - contestant_output: RawPath | None, + correct_output: IOutputPath, + contestant_output: IRawPath | None, ): super().__init__() self.job_managers = [] @@ -51,7 +51,7 @@ def verdict(self) -> OpendataVerdict: class InputManager(StatusJobManager): - def __init__(self, input_: InputPath, info: TestcaseInfo, seed: int | None): + def __init__(self, input_: IInputPath, info: TestcaseInfo, seed: int | None): super().__init__(f"Generate input {input_:n}") self._input = input_ self._info = info @@ -76,7 +76,7 @@ def _get_jobs(self) -> list[Job]: class OutputManager(StatusJobManager): - def __init__(self, input_: InputPath, info: TestcaseInfo, output: OutputPath): + def __init__(self, input_: IInputPath, info: TestcaseInfo, output: IOutputPath): self._input = input_ self._info = info self._output = output @@ -107,11 +107,11 @@ def _get_jobs(self) -> list[Job]: class CheckerManager(StatusJobManager): def __init__( self, - input_: InputPath, + input_: IInputPath, test: int, seed: int | None, - correct_output: OutputPath, - contestant_output: RawPath, + correct_output: IOutputPath, + contestant_output: IRawPath, ): self._input = input_ self._test = test diff --git a/pisek/task_jobs/checker/checker.py b/pisek/task_jobs/checker/checker.py index 5d44ebd6..95b7c5e2 100644 --- a/pisek/task_jobs/checker/checker.py +++ b/pisek/task_jobs/checker/checker.py @@ -13,7 +13,7 @@ from typing import Optional from pisek.env.env import Env -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.config.config_types import OutCheck, JudgeType from pisek.task_jobs.solution.solution_result import Verdict @@ -28,9 +28,9 @@ def checker_job( - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, test: int, seed: Optional[int], expected_verdict: Optional[Verdict], diff --git a/pisek/task_jobs/checker/checker_base.py b/pisek/task_jobs/checker/checker_base.py index 1353128d..5a3c8028 100644 --- a/pisek/task_jobs/checker/checker_base.py +++ b/pisek/task_jobs/checker/checker_base.py @@ -16,7 +16,7 @@ from functools import cache from pisek.utils.text import tab -from pisek.utils.paths import InputPath, OutputPath, LogPath +from pisek.utils.paths import IInputPath, IOutputPath, LogPath from pisek.env.env import Env from pisek.config.config_types import DataFormat from pisek.task_jobs.tools import SanitizationResultKind @@ -40,7 +40,7 @@ def __init__( name: str, test: int, checker_name: str, - input_: InputPath, + input_: IInputPath, checker_log_file: LogPath, expected_verdict: Optional[Verdict], **kwargs, @@ -216,9 +216,9 @@ def __init__( env: Env, checker_name: str, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, expected_verdict: Optional[Verdict], **kwargs, ) -> None: diff --git a/pisek/task_jobs/checker/cms_judge.py b/pisek/task_jobs/checker/cms_judge.py index 71477936..9394c150 100644 --- a/pisek/task_jobs/checker/cms_judge.py +++ b/pisek/task_jobs/checker/cms_judge.py @@ -16,7 +16,7 @@ from tempfile import gettempdir from uuid import uuid4 -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.env.env import Env from pisek.config.task_config import RunSection from pisek.config.config_types import ProgramRole @@ -101,9 +101,9 @@ def __init__( env: Env, judge: RunSection, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, expected_verdict: Optional[Verdict], **kwargs, ) -> None: diff --git a/pisek/task_jobs/checker/diff_checker.py b/pisek/task_jobs/checker/diff_checker.py index a5ceb9b6..35642101 100644 --- a/pisek/task_jobs/checker/diff_checker.py +++ b/pisek/task_jobs/checker/diff_checker.py @@ -15,7 +15,7 @@ from typing import Optional from pisek.env.env import Env -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.jobs.jobs import PipelineItemFailure from pisek.utils.text import tab from pisek.task_jobs.run_result import RunResult, RunResultKind @@ -34,9 +34,9 @@ def __init__( self, env: Env, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, expected_verdict: Optional[Verdict], ) -> None: super().__init__( diff --git a/pisek/task_jobs/checker/judgelib_checker.py b/pisek/task_jobs/checker/judgelib_checker.py index 4794e564..dca7ce2c 100644 --- a/pisek/task_jobs/checker/judgelib_checker.py +++ b/pisek/task_jobs/checker/judgelib_checker.py @@ -16,7 +16,7 @@ from typing import Optional from pisek.env.env import Env -from pisek.utils.paths import TaskPath, InputPath, OutputPath +from pisek.utils.paths import TaskPath, IInputPath, IOutputPath from pisek.jobs.jobs import PipelineItemFailure from pisek.utils.text import tab from pisek.task_jobs.run_result import RunResult, RunResultKind @@ -92,9 +92,9 @@ def __init__( self, env: Env, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, expected_verdict: Optional[Verdict], ) -> None: super().__init__( @@ -133,9 +133,9 @@ def __init__( self, env: Env, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, expected_verdict: Optional[Verdict], ) -> None: super().__init__( diff --git a/pisek/task_jobs/checker/opendata_judge.py b/pisek/task_jobs/checker/opendata_judge.py index d90f1a97..3604676e 100644 --- a/pisek/task_jobs/checker/opendata_judge.py +++ b/pisek/task_jobs/checker/opendata_judge.py @@ -15,7 +15,7 @@ import logging from typing import Any -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.env.env import Env from pisek.config.config_types import ProgramRole from pisek.config.task_config import RunSection @@ -52,9 +52,9 @@ def __init__( env: Env, judge: RunSection, test: int, - input_: InputPath, - output: OutputPath, - correct_output: OutputPath, + input_: IInputPath, + output: IOutputPath, + correct_output: IOutputPath, seed: int | None, expected_verdict: Verdict | None, **kwargs, diff --git a/pisek/task_jobs/data/data.py b/pisek/task_jobs/data/data.py index 5c148013..52f82dbe 100644 --- a/pisek/task_jobs/data/data.py +++ b/pisek/task_jobs/data/data.py @@ -16,7 +16,7 @@ from pisek.jobs.jobs import PipelineItemFailure from pisek.env.env import Env -from pisek.utils.paths import TaskPath, InputPath, OutputPath +from pisek.utils.paths import TaskPath, IInputPath, IOutputPath from pisek.task_jobs.task_job import TaskJob @@ -62,7 +62,7 @@ def _run(self): class InputSmall(DataJob): """Checks that input is small enough to download.""" - def __init__(self, env: Env, input_: InputPath, **kwargs) -> None: + def __init__(self, env: Env, input_: IInputPath, **kwargs) -> None: super().__init__( env=env, name=f"Input {input_:p} is smaller than {env.config.limits.input_max_size}MB", @@ -81,7 +81,7 @@ def _run(self): class OutputSmall(DataJob): """Checks that output is small enough to upload.""" - def __init__(self, env: Env, output: OutputPath, **kwargs) -> None: + def __init__(self, env: Env, output: IOutputPath, **kwargs) -> None: super().__init__( env=env, name=f"Output {output:p} is smaller than {env.config.limits.output_max_size}MB", diff --git a/pisek/task_jobs/data/testcase_info.py b/pisek/task_jobs/data/testcase_info.py index 1f086c4b..a3fa4ac8 100644 --- a/pisek/task_jobs/data/testcase_info.py +++ b/pisek/task_jobs/data/testcase_info.py @@ -16,7 +16,15 @@ from pisek.env.env import Env from pisek.task_jobs.task_job import TaskJob -from pisek.utils.paths import TESTS_DIR, INPUTS_LIST, InputPath, OutputPath, TaskPath +from pisek.utils.paths import ( + TESTS_DIR, + INPUTS_LIST, + InputPath, + IInputPath, + IOutputPath, + OutputPath, + TaskPath, +) class TestcaseGenerationMode(StrEnum): @@ -46,7 +54,7 @@ def static(name: str) -> "TestcaseInfo": def input_path( self, seed: int | None = None, solution: str | None = None - ) -> InputPath: + ) -> IInputPath: filename = self.name if self.seeded: assert seed is not None @@ -57,10 +65,11 @@ def input_path( def reference_output( self, env: Env, seed: int | None = None, solution: str | None = None - ) -> OutputPath: + ) -> IOutputPath: is_static = self.generation_mode == TestcaseGenerationMode.static input_path = self.input_path(seed, solution=env.config.primary_solution) + path: IOutputPath if is_static: path = OutputPath.static(input_path.replace_suffix(".out").name) else: diff --git a/pisek/task_jobs/generator/base_classes.py b/pisek/task_jobs/generator/base_classes.py index ef3a65bf..94e27652 100644 --- a/pisek/task_jobs/generator/base_classes.py +++ b/pisek/task_jobs/generator/base_classes.py @@ -16,7 +16,7 @@ from pisek.env.env import Env from pisek.jobs.jobs import PipelineItemFailure from pisek.config.task_config import RunSection -from pisek.utils.paths import InputPath +from pisek.utils.paths import IInputPath from pisek.task_jobs.task_job import TaskJob from pisek.task_jobs.program import ProgramsJob from pisek.task_jobs.data.testcase_info import TestcaseInfo, TestcaseGenerationMode @@ -45,7 +45,7 @@ def __init__( generator: RunSection, testcase_info: TestcaseInfo, seed: Optional[int], - input_path: InputPath, + input_path: IInputPath, *, name: str = "", **kwargs, diff --git a/pisek/task_jobs/generator/generator_manager.py b/pisek/task_jobs/generator/generator_manager.py index acd11db0..6f8c4678 100644 --- a/pisek/task_jobs/generator/generator_manager.py +++ b/pisek/task_jobs/generator/generator_manager.py @@ -18,7 +18,7 @@ from hashlib import blake2b from pisek.env.env import Env -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.config.config_types import GenType from pisek.config.task_config import RunSection from pisek.jobs.jobs import Job, JobManager @@ -91,7 +91,7 @@ def generate_input( def generate_input_direct( - env: Env, testcase_info: TestcaseInfo, seed: Optional[int], input_path: InputPath + env: Env, testcase_info: TestcaseInfo, seed: Optional[int], input_path: IInputPath ) -> GenerateInput: assert env.config.tests.in_gen is not None assert env.config.tests.gen_type is not None @@ -126,7 +126,7 @@ def generator_test_determinism( class TestcaseInfoMixin(JobManager): def __init__(self, name: str, **kwargs) -> None: self.inputs: dict[str, tuple[set[int], int | None]] = {} - self.input_dataset: set[InputPath] = set() + self.input_dataset: set[IInputPath] = set() self._gen_inputs_job: dict[Optional[int], GenerateInput] = {} self._jobs: list[Job] = [] @@ -240,7 +240,7 @@ def _add_generate_input_jobs( ) ) - def _validate(self, input_path: InputPath, test_num: int) -> ValidatorJob: + def _validate(self, input_path: IInputPath, test_num: int) -> ValidatorJob: assert self._env.config.tests.validator is not None assert self._env.config.tests.validator_type is not None @@ -283,7 +283,7 @@ def _add_respects_seed_jobs( for i in range(2): check_seeded.add_prerequisite(self._gen_inputs_job[seeds[i]]) - def _check_input_jobs(self, input_path: InputPath) -> None: + def _check_input_jobs(self, input_path: IInputPath) -> None: self._add_job( sanitize_job(self._env, input_path, True), new_last=True, @@ -294,7 +294,7 @@ def _check_input_jobs(self, input_path: InputPath) -> None: def _add_check_output_jobs( self, - output_path: OutputPath, + output_path: IOutputPath, ) -> None: self._add_job( sanitize_job(self._env, output_path, False), diff --git a/pisek/task_jobs/generator/opendata_v1.py b/pisek/task_jobs/generator/opendata_v1.py index 223211ad..7746eae6 100644 --- a/pisek/task_jobs/generator/opendata_v1.py +++ b/pisek/task_jobs/generator/opendata_v1.py @@ -15,7 +15,7 @@ from pisek.env.env import Env from pisek.config.config_types import ProgramRole from pisek.config.task_config import RunSection -from pisek.utils.paths import InputPath +from pisek.utils.paths import IInputPath from pisek.task_jobs.program import ProgramsJob, RunResultKind from pisek.task_jobs.data.testcase_info import TestcaseInfo @@ -45,7 +45,7 @@ class OpendataV1GeneratorJob(ProgramsJob): generator: RunSection seed: Optional[int] testcase_info: TestcaseInfo - input_path: InputPath + input_path: IInputPath def __init__(self, env: Env, *, name: str = "", **kwargs) -> None: super().__init__(env=env, name=name, **kwargs) diff --git a/pisek/task_jobs/generator/pisek_v1.py b/pisek/task_jobs/generator/pisek_v1.py index 7df8b556..2cc2e3d5 100644 --- a/pisek/task_jobs/generator/pisek_v1.py +++ b/pisek/task_jobs/generator/pisek_v1.py @@ -17,7 +17,7 @@ from pisek.env.env import Env from pisek.config.config_types import ProgramRole from pisek.config.task_config import RunSection -from pisek.utils.paths import TaskPath, InputPath, LogPath +from pisek.utils.paths import TaskPath, IInputPath, LogPath from pisek.task_jobs.program import ProgramsJob, RunResultKind from pisek.task_jobs.data.testcase_info import TestcaseInfo @@ -89,7 +89,7 @@ class PisekV1GeneratorJob(ProgramsJob): generator: RunSection seed: Optional[int] testcase_info: TestcaseInfo - input_path: InputPath + input_path: IInputPath def __init__(self, env: Env, *, name: str = "", **kwargs) -> None: super().__init__(env=env, name=name, **kwargs) diff --git a/pisek/task_jobs/solution/solution.py b/pisek/task_jobs/solution/solution.py index d35e506c..e8eb667a 100644 --- a/pisek/task_jobs/solution/solution.py +++ b/pisek/task_jobs/solution/solution.py @@ -18,7 +18,7 @@ from pisek.env.env import Env from pisek.jobs.jobs import State -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.config.config_types import ProgramRole from pisek.config.task_config import RunSection from pisek.task_jobs.program import RunResult, ProgramsJob @@ -65,8 +65,8 @@ def __init__( env: Env, solution: RunSection, is_primary: bool, - input_: InputPath, - output: OutputPath, + input_: IInputPath, + output: IOutputPath, **kwargs, ) -> None: super().__init__( @@ -98,7 +98,7 @@ def __init__( is_primary: bool, judge: RunSection, test: int, - input_: InputPath, + input_: IInputPath, expected_verdict: Optional[Verdict] = None, **kwargs, ): diff --git a/pisek/task_jobs/solution/solution_manager.py b/pisek/task_jobs/solution/solution_manager.py index daf62389..00a5cc65 100644 --- a/pisek/task_jobs/solution/solution_manager.py +++ b/pisek/task_jobs/solution/solution_manager.py @@ -20,7 +20,7 @@ from pisek.jobs.jobs import State, Job, PipelineItemFailure from pisek.env.env import Env -from pisek.utils.paths import InputPath +from pisek.utils.paths import IInputPath from pisek.config.config_types import TaskType from pisek.utils.text import pad, pad_left, tab from pisek.utils.terminal import MSG_LEN, right_aligned_text @@ -57,9 +57,9 @@ def _get_jobs(self) -> list[Job]: self.is_primary: bool = self._env.config.solutions[self.solution_label].primary self._solution = self._env.config.solutions[self.solution_label].run - self._sols: dict[InputPath, RunSolution] = {} - self._checkers: dict[InputPath, RunChecker] = {} - self._static_out_checkers: dict[InputPath, RunChecker] = {} + self._sols: dict[IInputPath, RunSolution] = {} + self._checkers: dict[IInputPath, RunChecker] = {} + self._static_out_checkers: dict[IInputPath, RunChecker] = {} for sub_num, inputs in self._all_testcases().items(): self.tests.append(TestJobGroup(self._env, sub_num)) @@ -209,7 +209,7 @@ def _create_batch_jobs( return (run_solution, run_checker) - def _create_interactive_jobs(self, inp: InputPath, test: int) -> RunInteractive: + def _create_interactive_jobs(self, inp: IInputPath, test: int) -> RunInteractive: """Create RunInteractive job for interactive task type.""" if self._env.config.tests.out_judge is None: raise RuntimeError("Unset judge for interactive.") diff --git a/pisek/task_jobs/task_manager.py b/pisek/task_jobs/task_manager.py index 62bb483e..9a8e2997 100644 --- a/pisek/task_jobs/task_manager.py +++ b/pisek/task_jobs/task_manager.py @@ -10,7 +10,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from pisek.utils.paths import InputPath, OutputPath +from pisek.utils.paths import IInputPath, IOutputPath from pisek.config.task_config import TestSection from pisek.jobs.status import StatusJobManager from pisek.task_jobs.task_job import TaskHelper @@ -29,7 +29,7 @@ class TaskJobManager(StatusJobManager, TaskHelper): """JobManager class that implements useful methods""" - def _get_samples(self) -> list[tuple[InputPath, OutputPath]]: + def _get_samples(self) -> list[tuple[IInputPath, IOutputPath]]: """Returns the list [(sample1.in, sample1.out), …].""" return [ ( diff --git a/pisek/task_jobs/tools.py b/pisek/task_jobs/tools.py index 063356d4..47244ea9 100644 --- a/pisek/task_jobs/tools.py +++ b/pisek/task_jobs/tools.py @@ -23,7 +23,7 @@ from pisek.jobs.jobs import Job, PipelineItemFailure from pisek.config.config_types import DataFormat from pisek.env.env import Env -from pisek.utils.paths import TaskPath, RawPath, SanitizedPath +from pisek.utils.paths import TaskPath, IRawPath, ISanitizedPath from pisek.task_jobs.task_job import TaskJob from pisek.task_jobs.task_manager import TaskJobManager from pisek.task_jobs.program import ProgramsJob @@ -174,7 +174,7 @@ class TextPreprocAbstract(ProgramsJob): """Abstract job that has method for file sanitization.""" def _run_text_preproc( - self, input_: RawPath, output: SanitizedPath + self, input_: IRawPath, output: ISanitizedPath ) -> SanitizationResult: try: os.remove(output.path) @@ -222,7 +222,7 @@ class Sanitize(SanitizeAbstract, TextPreprocAbstract): """Sanitize text file using Text Preprocessor.""" def __init__( - self, env: Env, input_: RawPath, output: SanitizedPath, **kwargs + self, env: Env, input_: IRawPath, output: ISanitizedPath, **kwargs ) -> None: super().__init__( env, input_, output, name=f"Sanitize {input_:p} -> {output:p}", **kwargs @@ -236,7 +236,7 @@ class IsClean(SanitizeAbstract, TextPreprocAbstract): """Check that file is same after sanitizing with Text Preprocessor.""" def __init__( - self, env: Env, input_: RawPath, output: SanitizedPath, **kwargs + self, env: Env, input_: IRawPath, output: ISanitizedPath, **kwargs ) -> None: super().__init__( env, input_, output, name=f"Check {input_:p} is clean", **kwargs @@ -257,14 +257,14 @@ def _sanitize_get_format(env: Env, is_input: bool) -> DataFormat: return env.config.tests.out_format -def sanitize_job(env: Env, path: SanitizedPath, is_input: bool) -> Job | None: +def sanitize_job(env: Env, path: ISanitizedPath, is_input: bool) -> Job | None: return sanitize_job_direct( env, path.to_raw(_sanitize_get_format(env, is_input)), path, is_input ) def sanitize_job_direct( - env: Env, path_from: RawPath, path_to: SanitizedPath, is_input: bool + env: Env, path_from: IRawPath, path_to: ISanitizedPath, is_input: bool ) -> Job | None: format_ = _sanitize_get_format(env, is_input) diff --git a/pisek/task_jobs/validator/validator_base.py b/pisek/task_jobs/validator/validator_base.py index 461bd7a4..434af89c 100644 --- a/pisek/task_jobs/validator/validator_base.py +++ b/pisek/task_jobs/validator/validator_base.py @@ -13,7 +13,7 @@ from abc import abstractmethod from pisek.env.env import Env -from pisek.utils.paths import InputPath +from pisek.utils.paths import IInputPath from pisek.config.task_config import ProgramRole, RunSection from pisek.task_jobs.run_result import RunResult from pisek.task_jobs.program import ProgramsJob @@ -26,7 +26,7 @@ def __init__( self, env: Env, validator: RunSection, - input_: InputPath, + input_: IInputPath, test: int, **kwargs, ): diff --git a/pisek/utils/paths.py b/pisek/utils/paths.py index 8226a2fd..c94b922d 100644 --- a/pisek/utils/paths.py +++ b/pisek/utils/paths.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from abc import ABC, abstractmethod from dataclasses import dataclass import os from typing import TYPE_CHECKING @@ -115,19 +116,75 @@ def generated_path(*path: str) -> "TaskPath": return TaskPath.data_path(GENERATED_SUBDIR, *path) -class JudgeablePath(TaskPath): +# ----- interfaces ----- + + +class IJudgeablePath(TaskPath, ABC): + @abstractmethod + def to_checker_log(self, judge: str) -> "LogPath": + pass + + +class ISanitizedPath(TaskPath, ABC): + @abstractmethod + def to_raw(self, format: DataFormat) -> "IRawPath": + pass + + +class IInputPath(ISanitizedPath): + @abstractmethod + def to_second(self) -> "IInputPath": + pass + + @abstractmethod + def to_output(self) -> "IOutputPath": + pass + + @abstractmethod + def to_log(self, program: str) -> "LogPath": + pass + + +class IOutputPath(IJudgeablePath, ISanitizedPath): + @abstractmethod + def to_reference_output(self) -> "IOutputPath": + pass + + @abstractmethod + def to_fuzzing(self, seed: int) -> "IOutputPath": + pass + + +class IRawPath(TaskPath, ABC): + @abstractmethod + def to_sanitized_input(self) -> IInputPath: + pass + + @abstractmethod + def to_sanitized_output(self) -> IOutputPath: + pass + + @abstractmethod + def to_sanitization_log(self) -> "LogPath": + pass + + +# ----- paths inside task ----- + + +class JudgeablePath(IJudgeablePath): def to_checker_log(self, judge: str) -> "LogPath": return LogPath(self.replace_suffix(f".{os.path.basename(judge)}.log").path) -class SanitizedPath(TaskPath): +class SanitizedPath(ISanitizedPath): def to_raw(self, format: DataFormat) -> "RawPath": if format == DataFormat.binary: return RawPath(self.path) return RawPath(self.path + ".raw") -class InputPath(SanitizedPath): +class InputPath(SanitizedPath, IInputPath): @staticmethod def new(*path: str, solution: str | None = None) -> "InputPath": if solution is None: @@ -135,25 +192,25 @@ def new(*path: str, solution: str | None = None) -> "InputPath": else: return InputPath(TESTS_DIR, solution, *path) - def to_second(self) -> "InputPath": + def to_second(self) -> IInputPath: return InputPath(self.replace_suffix(".in2").path) - def to_output(self) -> "OutputPath": + def to_output(self) -> IOutputPath: return OutputPath(self.replace_suffix(f".out").path) def to_log(self, program: str) -> "LogPath": return LogPath(self.replace_suffix(f".{os.path.basename(program)}.log").path) -class OutputPath(JudgeablePath, SanitizedPath): +class OutputPath(JudgeablePath, SanitizedPath, IOutputPath): @staticmethod def static(*path) -> "OutputPath": return OutputPath(TESTS_DIR, INPUTS_SUBDIR, *path) - def to_reference_output(self) -> "OutputPath": + def to_reference_output(self) -> IOutputPath: return OutputPath(self.replace_suffix(f".ok").path) - def to_fuzzing(self, seed: int) -> "OutputPath": + def to_fuzzing(self, seed: int) -> IOutputPath: return OutputPath( TESTS_DIR, FUZZING_OUTPUTS_SUBDIR, @@ -167,34 +224,46 @@ def generator_log(generator: str) -> "LogPath": return LogPath(TESTS_DIR, INPUTS_SUBDIR, f"{os.path.basename(generator)}.log") -class RawPath(TaskPath): - def to_sanitized_output(self) -> OutputPath: - return OutputPath(self.path.removesuffix(".raw")) - - def to_sanitized_input(self) -> InputPath: +class RawPath(IRawPath): + def to_sanitized_input(self) -> IInputPath: return InputPath(self.path.removesuffix(".raw")) + def to_sanitized_output(self) -> IOutputPath: + return OutputPath(self.path.removesuffix(".raw")) + def to_sanitization_log(self) -> LogPath: return LogPath(self.replace_suffix(".sanitizer.log").path) -class OpendataPath(TaskPath): +# ----- opendata paths = paths outside task ----- + + +class OpendatadPath(TaskPath): def __init__(self, tmp_dir: str, *path: str): self._tmp_dir = tmp_dir super().__init__(*path) - def to_raw(self, format: DataFormat) -> "RawPath": + +class OpendataJudgeablePath(OpendatadPath, IJudgeablePath): + def to_checker_log(self, judge: str) -> "LogPath": + return LogPath( + self._tmp_dir, self.replace_suffix(f".{os.path.basename(judge)}.log").name + ) + + +class OpendataSanitizedPath(OpendatadPath, ISanitizedPath): + def to_raw(self, format: DataFormat) -> IRawPath: if format == DataFormat.binary: return OpendataRawPath(self._tmp_dir, self.path) return RawPath(self._tmp_dir, self.name + ".raw") -class OpendataInputPath(OpendataPath, InputPath): - def to_second(self) -> "InputPath": - assert False +class OpendataInputPath(OpendataSanitizedPath, IInputPath): + def to_second(self) -> IInputPath: + return InputPath(self._tmp_dir, self.replace_suffix(".in2").name) - def to_output(self) -> "OutputPath": - assert False + def to_output(self) -> IOutputPath: + return OutputPath(self._tmp_dir, self.replace_suffix(f".out").name) def to_log(self, program: str) -> "LogPath": return LogPath( @@ -202,20 +271,23 @@ def to_log(self, program: str) -> "LogPath": ) -class OpendataOutputPath(OpendataPath, OutputPath): - def to_reference_output(self) -> "OutputPath": - assert False +class OpendataOutputPath(OpendataSanitizedPath, OpendataJudgeablePath, IOutputPath): + def to_reference_output(self) -> IOutputPath: + return OutputPath(self._tmp_dir, self.replace_suffix(f".ok").name) + + def to_fuzzing(self, seed: int) -> IOutputPath: + return OutputPath( + self._tmp_dir, + self.replace_suffix(f".{seed:x}.out").name, + ) - def to_fuzzing(self, seed: int) -> "OutputPath": - assert False +class OpendataRawPath(OpendataSanitizedPath, IRawPath): + def to_sanitized_input(self) -> IInputPath: + return InputPath(self._tmp_dir, self.name.removesuffix(".raw")) -class OpendataRawPath(OpendataPath, RawPath): - def to_sanitized_output(self) -> OutputPath: + def to_sanitized_output(self) -> IOutputPath: return OutputPath(self._tmp_dir, self.name.removesuffix(".raw")) - def to_sanitized_input(self) -> InputPath: - assert False - - def to_sanitization_log(self): + def to_sanitization_log(self) -> LogPath: return LogPath(self._tmp_dir, self.replace_suffix(".sanitizer.log").name)