Coverage for src / competitive_verifier / models / problem.py: 100%
28 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
1import hashlib
2import pathlib
3from abc import ABC, abstractmethod
4from collections.abc import Iterable
5from typing import NamedTuple, Optional, cast
7from competitive_verifier import config
10class TestCaseFile(NamedTuple):
11 name: str
12 input_path: pathlib.Path
13 output_path: pathlib.Path
16class TestCaseData(NamedTuple):
17 name: str
18 input_data: bytes
19 output_data: bytes
22class TestCaseProvider(ABC):
23 @abstractmethod
24 def download_system_cases(self) -> Iterable[TestCaseData] | bool: ...
26 @abstractmethod
27 def iter_system_cases(self) -> Iterable[TestCaseFile]: ...
29 @property
30 def checker(self) -> pathlib.Path | None:
31 return None
34class Problem(TestCaseProvider):
35 def __repr__(self) -> str:
36 return f"{self.__class__.__name__}.from_url({self.url!r})"
38 def __hash__(self) -> int: # pragma: no cover
39 return hash(self.url) ^ hash(type(self))
41 def __eq__(self, value: object) -> bool:
42 if type(self) is not type(value):
43 return False
44 return self.url == cast("Problem", value).url
46 @property
47 @abstractmethod
48 def url(self) -> str: ...
50 @classmethod
51 @abstractmethod
52 def from_url(cls, url: str) -> Optional["Problem"]: ...
54 @property
55 def hash_id(self):
56 return hashlib.md5(self.url.encode(), usedforsecurity=False).hexdigest()
58 @property
59 def problem_directory(self):
60 return config.get_problem_cache_dir() / self.hash_id
62 @property
63 def test_directory(self):
64 return self.problem_directory / "test"