diff --git a/algobattle/cli.py b/algobattle/cli.py index c97e1532..da371128 100644 --- a/algobattle/cli.py +++ b/algobattle/cli.py @@ -52,7 +52,17 @@ from algobattle.match import AlgobattleConfig, EmptyUi, Match, MatchConfig, MatchupStr, Ui, ProjectConfig from algobattle.problem import Instance, Problem, Solution from algobattle.program import Generator, Matchup, Solver -from algobattle.util import BuildError, EncodableModel, ExceptionInfo, Role, RunningTimer, BaseModel, TempDir, timestamp +from algobattle.util import ( + BuildError, + DockerNotRunning, + EncodableModel, + ExceptionInfo, + Role, + RunningTimer, + BaseModel, + TempDir, + timestamp, +) from algobattle.templates import Language, PartialTemplateArgs, TemplateArgs, write_problem_template, write_templates @@ -170,11 +180,14 @@ def run_match( try: with CliUi() if ui else EmptyUi() as ui_obj: run_async_fn(result.run, config, ui_obj) + except DockerNotRunning: + console.print("[error]Could not connect to the Docker Daemon.[/] Is Docker running?") + save = False except KeyboardInterrupt: console.print("[error]Stopping match execution") finally: try: - if config.project.points > 0: + if config.project.points > 0 and result.active_teams: points = result.calculate_points(config.project.points) leaderboard = Table( Column("Team", justify="center"), diff --git a/algobattle/problem.py b/algobattle/problem.py index 65cabace..6d19f7a7 100644 --- a/algobattle/problem.py +++ b/algobattle/problem.py @@ -20,6 +20,7 @@ overload, cast, get_args, + runtime_checkable, ) from math import inf, isnan from annotated_types import GroupedMetadata @@ -132,6 +133,7 @@ def maximize(function: Callable[P, float]) -> Callable[P, float]: _S = TypeVar("_S", bound=Solution[Instance], contravariant=True) +@runtime_checkable class ScoreFunctionWithSol(Protocol, Generic[_I, _S]): """Type of `score` function passed to Problem if `with_solution` is set.""" @@ -150,6 +152,7 @@ def __call__(self, instance: _I, *, generator_solution: _S, solver_solution: _S) ... +@runtime_checkable class ScoreFunctionNoSol(Protocol, Generic[_I, _S]): """Type of `score` function passed to Problem if `with_solution` is not set.""" @@ -517,9 +520,11 @@ def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreS if cls._validate_with_self(model_type): def validate_with_self(input: object, validate: ValidatorFunctionWrapHandler, info: ValidationInfo) -> Self: - self = validate(input) + self: Self = validate(input) if info.context is None or "self" not in info.context: - self = cls.model_validate(input, context=(info.context or {}) | {"self": self, model_type: self}) + self = cast( + Self, cls.model_validate(input, context=(info.context or {}) | {"self": self, model_type: self}) + ) return self schema = with_info_wrap_validator_function(validate_with_self, schema) diff --git a/algobattle/program.py b/algobattle/program.py index e836cee8..9788dae1 100644 --- a/algobattle/program.py +++ b/algobattle/program.py @@ -28,6 +28,7 @@ from algobattle.util import ( BuildError, DockerError, + DockerNotRunning, Encodable, EncodingError, ExceptionInfo, @@ -55,7 +56,7 @@ def client() -> DockerClient: else: _client_var.ping() except (DockerException, APIError): - raise SystemExit("Could not connect to the docker daemon. Is docker running?") + raise DockerNotRunning return _client_var diff --git a/algobattle/util.py b/algobattle/util.py index f7691eaa..f093972d 100644 --- a/algobattle/util.py +++ b/algobattle/util.py @@ -182,6 +182,10 @@ class DockerError(AlgobattleBaseException): """Indicates that an issue with the docker daemon occured.""" +class DockerNotRunning(BaseException): + """Indicates that the Docker Daemon is not running.""" + + class ExceptionInfo(BaseModel): """Details about an exception that was raised."""