Coverage for sel_tools/code_evaluation/jobs/factory.py: 90%
39 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-04 21:22 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-04 21:22 +0000
1"""Evaluation Job Creation Module."""
3import importlib.util
4import inspect
5import sys
6from abc import abstractmethod
7from collections.abc import Generator
8from contextlib import contextmanager
9from importlib.abc import Loader
10from pathlib import Path
12from sel_tools.code_evaluation.jobs.common import EvaluationJob
13from sel_tools.utils.repo import GitlabProject
16@contextmanager
17def add_temporarily_to_pythonpath(folder: Path) -> Generator:
18 """Add folder temporarily to pythonpath."""
19 folder_to_add = str(folder.resolve())
20 sys.path.append(folder_to_add)
21 yield
22 sys.path.remove(folder_to_add)
25class EvaluationJobFactory:
26 """Evaluation Job Factory.
28 Implement the create function to return the list of evaluation jobs that
29 should be used for the given homework number.
30 """
32 @staticmethod
33 @abstractmethod
34 def create(gitlab_projects: list[GitlabProject], homework_number: int) -> list[EvaluationJob]:
35 msg = "Don't call me, I'm abstract."
36 raise NotImplementedError(msg)
38 @staticmethod
39 def load_factory_from_file(module_path: Path) -> type["EvaluationJobFactory"]:
40 spec = importlib.util.spec_from_file_location(module_path.stem, module_path)
41 if spec is None:
42 msg = f"No subclass of EvaluationJobFactory in {module_path}"
43 raise ImportError(msg)
44 module = importlib.util.module_from_spec(spec)
45 # To enable loading files with additionally required python files lying besides them
46 with add_temporarily_to_pythonpath(module_path.parent):
47 if isinstance(spec.loader, Loader):
48 spec.loader.exec_module(module)
49 for name, attribute in module.__dict__.items():
50 # Feel free to adapt the below conditions to your use case
51 if name.startswith("_") or not inspect.isclass(attribute):
52 continue
53 if issubclass(attribute, EvaluationJobFactory) and name != "EvaluationJobFactory":
54 return attribute
55 msg = f"No subclass of EvaluationJobFactory in {module_path}"
56 raise ModuleNotFoundError(msg)