Skip to content

engine

engine

Engine

Bases: ABC

Source code in src/qgis_server_light/worker/engine.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class Engine(ABC):
    def __init__(
        self,
        context: EngineContext,
        runner_plugins: list[str],
        svg_paths: Optional[List[str]] = None,
        log_level=logging.WARNING,
    ):
        self.qgis = Qgis(svg_paths, log_level)
        self.context = context
        self.layer_cache: dict[Any, Any] = {}
        self.available_runner_classes: dict[str, Type[Runner]] = {}
        self.available_runner_classes_by_job_info: dict[str, Type[Runner]] = {}
        self.available_job_info_classes: dict[str, Type[QslJobInfoParameter]] = {}
        self._load_runner_plugins(runner_plugins)
        logging.debug(self.available_runner_classes)
        logging.debug(self.available_runner_classes_by_job_info)
        logging.debug(self.available_job_info_classes)
        self.info = self._initialize_infos()

    def __del__(self):
        self.qgis.exitQgis()

    def _load_runner_plugins(self, worker_plugins: list[str]):
        for path in worker_plugins:
            loaded_class = self._load_runner_class(path)
            if loaded_class is not None:
                self.available_runner_classes[path] = loaded_class
                self.available_runner_classes_by_job_info[
                    loaded_class.job_info_class.__name__
                ] = loaded_class
                self.available_job_info_classes[
                    loaded_class.job_info_class.__name__
                ] = loaded_class.job_info_class

    @staticmethod
    def _load_runner_class(path: str) -> Type[Runner] | None:
        """
        Loads a class dynamically at runtime, like:
        "mypackage.mymodule.MyClass"
        """

        module_path, class_name = path.rsplit(".", 1)
        module = importlib.import_module(module_path)
        cls = getattr(module, class_name, None)

        # Ensure the class was loaded correctly
        if cls is None:
            raise ImportError(
                f"Class '{class_name}' not found in module '{module_path}'."
            )
        if not inspect.isclass(cls):
            raise TypeError(f"Passed '{class_name}' is not a class.")

        if not issubclass(cls, Runner):
            raise TypeError(
                f"{cls.__name__} is not a plugin as expected (each plugin has to inherit from qgis_server_light.worker.job.common.Job)."
            )

        return cls

    def _initialize_infos(self):
        worker_info = EngineInfo(
            id=str(uuid.uuid4()),
            qgis_info=QgisInfo(
                version=version(),
                version_name=version_name(),
                path=self.qgis.prefixPath(),
            ),
            status=Status.STARTING,
            started=datetime.datetime.now().timestamp(),
        )
        logging.debug(json.dumps(asdict(worker_info), indent=2))
        return worker_info

    def runner_plugin_by_job_info(self, job_info: QslJobInfoParameter) -> Type[Runner]:
        """
        Here we decide which plugin we load dynamically out of the available ones.

        Args:
            job_info: Is the parameter instance we check the available worker classes and there the
                job_info_class at each.

        Returns:
            The selected runner class
        """
        try:
            return self.available_runner_classes_by_job_info[
                job_info.__class__.__name__
            ]
        except KeyError:
            raise RuntimeError(f"Type {type(job_info)} not supported")

    def process(self, job_info: QslJobInfoParameter) -> JobResult:
        runner_class = self.runner_plugin_by_job_info(job_info)
        runner = runner_class(
            self.qgis,
            JobContext(self.context.base_path),
            job_info,
            layer_cache=self.layer_cache,
        )
        return runner.run()

    @property
    def status(self):
        return self.info.status.value

    def set_waiting(self):
        self.info.status = Status.WAITING

    def set_crashed(self):
        self.info.status = Status.CRASHED

    def set_processing(self):
        self.info.status = Status.PROCESSING

available_job_info_classes: dict[str, Type[QslJobInfoParameter]] = {} instance-attribute

available_runner_classes: dict[str, Type[Runner]] = {} instance-attribute

available_runner_classes_by_job_info: dict[str, Type[Runner]] = {} instance-attribute

context = context instance-attribute

info = self._initialize_infos() instance-attribute

layer_cache: dict[Any, Any] = {} instance-attribute

qgis = Qgis(svg_paths, log_level) instance-attribute

status property

__del__()

Source code in src/qgis_server_light/worker/engine.py
48
49
def __del__(self):
    self.qgis.exitQgis()

__init__(context: EngineContext, runner_plugins: list[str], svg_paths: Optional[List[str]] = None, log_level=logging.WARNING)

Source code in src/qgis_server_light/worker/engine.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def __init__(
    self,
    context: EngineContext,
    runner_plugins: list[str],
    svg_paths: Optional[List[str]] = None,
    log_level=logging.WARNING,
):
    self.qgis = Qgis(svg_paths, log_level)
    self.context = context
    self.layer_cache: dict[Any, Any] = {}
    self.available_runner_classes: dict[str, Type[Runner]] = {}
    self.available_runner_classes_by_job_info: dict[str, Type[Runner]] = {}
    self.available_job_info_classes: dict[str, Type[QslJobInfoParameter]] = {}
    self._load_runner_plugins(runner_plugins)
    logging.debug(self.available_runner_classes)
    logging.debug(self.available_runner_classes_by_job_info)
    logging.debug(self.available_job_info_classes)
    self.info = self._initialize_infos()

process(job_info: QslJobInfoParameter) -> JobResult

Source code in src/qgis_server_light/worker/engine.py
121
122
123
124
125
126
127
128
129
def process(self, job_info: QslJobInfoParameter) -> JobResult:
    runner_class = self.runner_plugin_by_job_info(job_info)
    runner = runner_class(
        self.qgis,
        JobContext(self.context.base_path),
        job_info,
        layer_cache=self.layer_cache,
    )
    return runner.run()

runner_plugin_by_job_info(job_info: QslJobInfoParameter) -> Type[Runner]

Here we decide which plugin we load dynamically out of the available ones.

Parameters:

  • job_info (QslJobInfoParameter) –

    Is the parameter instance we check the available worker classes and there the job_info_class at each.

Returns:

  • Type[Runner]

    The selected runner class

Source code in src/qgis_server_light/worker/engine.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def runner_plugin_by_job_info(self, job_info: QslJobInfoParameter) -> Type[Runner]:
    """
    Here we decide which plugin we load dynamically out of the available ones.

    Args:
        job_info: Is the parameter instance we check the available worker classes and there the
            job_info_class at each.

    Returns:
        The selected runner class
    """
    try:
        return self.available_runner_classes_by_job_info[
            job_info.__class__.__name__
        ]
    except KeyError:
        raise RuntimeError(f"Type {type(job_info)} not supported")

set_crashed()

Source code in src/qgis_server_light/worker/engine.py
138
139
def set_crashed(self):
    self.info.status = Status.CRASHED

set_processing()

Source code in src/qgis_server_light/worker/engine.py
141
142
def set_processing(self):
    self.info.status = Status.PROCESSING

set_waiting()

Source code in src/qgis_server_light/worker/engine.py
135
136
def set_waiting(self):
    self.info.status = Status.WAITING

EngineContext dataclass

Source code in src/qgis_server_light/worker/engine.py
23
24
25
@dataclass
class EngineContext:
    base_path: Union[str, pathlib.Path]

base_path: Union[str, pathlib.Path] instance-attribute

__init__(base_path: Union[str, pathlib.Path]) -> None