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

1"""Evaluation Job Creation Module.""" 

2 

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 

11 

12from sel_tools.code_evaluation.jobs.common import EvaluationJob 

13from sel_tools.utils.repo import GitlabProject 

14 

15 

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) 

23 

24 

25class EvaluationJobFactory: 

26 """Evaluation Job Factory. 

27 

28 Implement the create function to return the list of evaluation jobs that 

29 should be used for the given homework number. 

30 """ 

31 

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) 

37 

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)