david22guy's picture
Upload folder using huggingface_hub
8d1e50f verified
Raw
History Blame Contribute Delete
4.31 kB
from __future__ import annotations
from abc import ABC, abstractmethod
import collections.abc
import json
from pathlib import Path
import typing
from typing import *
class Challenge[P, S, V](ABC):
@abstractmethod
def generate(self, seed: int) -> tuple[P, V]:
raise NotImplementedError()
@abstractmethod
def verify(self, problem: P, solution: S, secrets: V) -> bool:
raise NotImplementedError()
class Solver[P, S](ABC):
@abstractmethod
def solve(self, problem: P) -> S:
raise NotImplementedError()
def _is_callable_hint(ty: Any) -> bool:
return typing.get_origin(ty) is collections.abc.Callable or ty is collections.abc.Callable
def _is_serde(ty: type) -> bool:
return isinstance(ty, type) and issubclass(ty, Serde) and (ty is not Serde)
def _convert_from(val: Any, ty: Any) -> Any:
if ty is Any:
return val
origin = typing.get_origin(ty)
args = typing.get_args(ty)
if origin is Union:
if val is None and type(None) in args:
return None
for variant in args:
if variant is type(None):
continue
try:
return _convert_from(val, variant)
except (TypeError, KeyError):
continue
raise TypeError(f'value {val!r} does not match any variant of {ty}')
if origin is list:
if not isinstance(val, list):
raise TypeError(f'expected list, got {type(val)}')
if args:
return [_convert_from(item, args[0]) for item in val]
return val
if origin is dict:
if not isinstance(val, dict):
raise TypeError(f'expected dict, got {type(val)}')
if len(args) == 2:
return {_convert_from(k, args[0]): _convert_from(v, args[1]) for k, v in val.items()}
return val
if origin is tuple:
if not isinstance(val, (list, tuple)):
raise TypeError(f'expected tuple, got {type(val)}')
if args:
return tuple((_convert_from(item, a) for item, a in zip(val, args)))
return tuple(val)
if _is_serde(ty):
if isinstance(val, ty):
return val
if isinstance(val, dict):
return ty.from_dict(val)
raise TypeError(f'expected dict or {ty.__name__}, got {type(val)}')
if isinstance(ty, type) and (not isinstance(val, ty)):
raise TypeError(f'expected type `{ty.__name__}` but got `{type(val).__name__}`')
return val
def _convert_to(val: Any) -> Any:
if isinstance(val, Serde):
return val.to_dict()
if isinstance(val, list):
return [_convert_to(item) for item in val]
if isinstance(val, dict):
return {k: _convert_to(v) for k, v in val.items()}
if isinstance(val, tuple):
return [_convert_to(item) for item in val]
return val
class Serde:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Self:
hints = typing.get_type_hints(cls)
defaults = {key: val for key in dir(cls) if not key.startswith('__') and (not isinstance((val := getattr(cls, key)), Callable))}
args = list()
for key, ty in hints.items():
if _is_callable_hint(ty):
continue
if key in data:
val = data[key]
elif key in defaults:
val = defaults[key]
else:
raise KeyError(f"missing expected key '{key}'")
args.append(_convert_from(val, ty))
return cls(*args)
def to_dict(self) -> dict[str, Any]:
hints = typing.get_type_hints(type(self))
result = {}
for key, ty in hints.items():
if _is_callable_hint(ty):
continue
result[key] = _convert_to(getattr(self, key))
return result
@classmethod
def from_json(cls, json_str: str) -> Self:
return cls.from_dict(json.loads(json_str))
def to_json(self) -> str:
return json.dumps(self.to_dict())
@classmethod
def from_json_file(cls, json_file: Path) -> Self:
with json_file.open('r') as infile:
return cls.from_dict(json.load(infile))
def to_json_file(self, out: Path) -> None:
with out.open('w') as outfile:
json.dump(self.to_dict(), outfile)