Source code for grader.checks.abstract_check

"""
Module containing a class representing an abstract check.
Each check should inherit from this class.
"""

from dataclasses import dataclass
import logging
from abc import ABC, abstractmethod
from typing import TypeVar, Generic

from grader.utils.logger import VERBOSE
from grader.utils.virtual_environment import VirtualEnvironment

logger = logging.getLogger("grader")


T = TypeVar("T")


[docs] @dataclass class CheckResult(Generic[T]): """ Class representing the result of a check. """ name: str result: T
[docs] class AbstractCheck(ABC, Generic[T]): """ Each check has a name and a project root path. """ def __init__(self, name: str, project_root: str, is_venv_required: bool = False): self._name = name self._project_root = project_root self._is_venv_required = is_venv_required
[docs] @abstractmethod def run(self) -> CheckResult[T]: # TODO - Check if we need the Optional """ Main method that executes the check. :returns: The result of the check. :rtype: Optional[T] """
@property def name(self) -> str: """ Get the name of the check. :returns: The name of the check. :rtype: str """ return self._name
[docs] @staticmethod def is_running_within_venv() -> bool: """ Determine if the check is running within a virtual environment. :returns: True if running within a virtual environment, False otherwise. :rtype: bool """ return VirtualEnvironment.is_initialized
def _pre_run(self) -> None: """ Pre-run checks to ensure the environment is set up correctly. :raises CheckError: If the check requires a virtual environment and is not running within one. """ if self._is_venv_required and not self.is_running_within_venv(): raise CheckError("Virtual environment is required for this check") logger.log(VERBOSE, "Running %s", self.name)
[docs] @dataclass class ScoredCheckResult(CheckResult[T]): """ Class representing the result of a scored check. """ max_score: int
[docs] @dataclass class NonScoredCheckResult(CheckResult[bool]): """ Class representing the result of a non-scored check. """
[docs] class ScoredCheck(AbstractCheck[float]): """ Each scored check has a maximum amount of points. """ def __init__(self, name: str, max_points: int, project_root: str, is_venv_requred: bool = False): super().__init__(name, project_root, is_venv_requred) self._max_points = max_points @property def max_points(self) -> int: """ :returns: The maximum amount of points that can be achieved by the check. :rtype: int """ return self._max_points
[docs] class NonScoredCheck(AbstractCheck[bool]): """ Non-scored checks do not have a maximum amount of points. """ def __init__(self, name: str, project_root: str, is_fatal: bool, is_venv_requred: bool = False): super().__init__(name, project_root, is_venv_requred) self._is_fatal = is_fatal @property def is_fatal(self) -> bool: """ :returns: True if the check failing is fatal, False otherwise. :rtype: bool """ return self._is_fatal
[docs] class CheckError(Exception): """Custom exception for check errors."""