[fix] clarify the mess of Engine.setup and Engine.init (#6343)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
This commit is contained in:
Markus Heiser
2026-06-30 19:22:23 +02:00
committed by GitHub
parent c5b1d066e5
commit d115c61a70
4 changed files with 52 additions and 28 deletions

View File

@@ -361,37 +361,43 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
https: socks5://proxy:port https: socks5://proxy:port
""" """
def setup(self, engine_settings: dict[str, t.Any]) -> bool: # pylint: disable=unused-argument def setup(self, engine_settings: dict[str, t.Any]) -> bool | None: # pylint: disable=unused-argument
"""Dynamic setup of the engine settings. """Dynamic setup of the engine settings.
With this method, the engine's setup is carried out. For example, to With this method, the engine's setup is carried out. For example, to
check or dynamically adapt the values handed over in the parameter check or dynamically adapt the values handed over in the parameter
``engine_settings``. The return value (True/False) indicates whether ``engine_settings``.
the setup was successful and the engine can be built or rejected.
The method is optional and is called synchronously as part of the Whether the initialization was successful can be indicated by the return
value ``True`` or even ``False``.
- If no return value (``None`` ) is given from this method , this is
equivalent to ``True``.
- If an exception is thrown as part of the initialization, this is
equivalent to ``False``.
The method is optional and is called **synchronously** as part of the
initialization of the service and is therefore only suitable for simple initialization of the service and is therefore only suitable for simple
(local) exams/changes at the engine setting. The :py:obj:`Engine.init` (local) exams/changes at the engine setting.
method must be used for longer tasks in which values of a remote must be
determined, for example. The :py:obj:`Engine.init` method must be used for longer tasks in which
values of a remote must be determined, for example.
""" """
return True return True
def init(self, engine_settings: dict[str, t.Any]) -> bool | None: # pylint: disable=unused-argument def init(self, engine_settings: dict[str, t.Any]) -> bool | None: # pylint: disable=unused-argument
"""Initialization of the engine. """Initialization of the engine.
The method is optional and asynchronous (in a thread). It is suitable, The method is optional and called **asynchronous** (in a thread). The
for example, for setting up a cache (for the engine) or for querying method is comparable to :py:obj:`Engine.setup`, it is suitable, for
values (required by the engine) from a remote. caching data that first needs to be requested from a remote.
Whether the initialization was successful can be indicated by the return The method is optional and runs **asynchronously** (in a thread), it is
value ``True`` or even ``False``. comparable to :py:obj:`Engine.setup`. For instance, it is suitable for
caching data that first needs to be requested from a remote source.
- If no return value is given from this init method (``None``), this is The evaluation of the return value is analogous to :py:obj:`Engine.setup`.
equivalent to ``True``.
- If an exception is thrown as part of the initialization, this is
equivalent to ``False``.
""" """
return True return True

View File

@@ -269,21 +269,27 @@ def is_engine_active(engine: "Engine | types.ModuleType"):
def call_engine_setup(engine: "Engine | types.ModuleType", engine_data: dict[str, t.Any]) -> bool: def call_engine_setup(engine: "Engine | types.ModuleType", engine_data: dict[str, t.Any]) -> bool:
setup_ok = False
setup_ok: bool | None = False
setup_func = getattr(engine, "setup", None) setup_func = getattr(engine, "setup", None)
if setup_func is None: if setup_func is None:
setup_ok = True setup_ok = True
elif not callable(setup_func): elif not callable(setup_func):
logger.error("engine's setup method isn't a callable (is of type: %s)", type(setup_func)) logger.error(f"engine's setup method isn't a callable (is of type: {type(setup_func)})")
else: else:
try: try:
setup_ok = engine.setup(engine_data) setup_ok = engine.setup(engine_data)
except Exception as e: # pylint: disable=broad-except except Exception as e: # pylint: disable=broad-except
logger.exception('exception : {0}'.format(e)) logger.exception(f"(PID {os.getpid()}) {engine.name}: engine SETUP failed, exception: {e}")
setup_ok = False
# The evaluation of the return value is analogous to Engine.init
if setup_ok is None:
setup_ok = True
if not setup_ok: if not setup_ok:
logger.error("%s: Engine setup was not successful, engine is set to inactive.", engine.name) logger.error(f"(PID {os.getpid()}) {engine.name}: engine setup was not successful")
return setup_ok return setup_ok
@@ -311,14 +317,16 @@ def load_engines(engine_list: list[dict[str, t.Any]]):
for engine_data in engine_list: for engine_data in engine_list:
if engine_data.get("inactive") is True: if engine_data.get("inactive") is True:
continue continue
engine = load_engine(engine_data) engine = load_engine(engine_data)
if engine: if engine:
register_engine(engine) register_engine(engine)
else: else:
# if an engine can't be loaded (if for example the engine is missing # if an engine can't be loaded (if for example the engine is missing
# tor or some other requirements) its set to inactive! # tor or some other requirements) its set to inactive!
logger.error( logger.error(
f"(PID {os.getpid()}) loading engine %s failed: set engine to inactive!", engine_data.get("name", "???") f"(PID {os.getpid()}) {engine_data.get('name', '???')}: can't register engine (loading engine failed)"
) )
engine_data["inactive"] = True engine_data["inactive"] = True
return engines return engines

View File

@@ -16,6 +16,7 @@ __all__ = [
import typing as t import typing as t
import os
from searx import logger from searx import logger
from searx import engines from searx import engines
@@ -92,7 +93,9 @@ class ProcessorMap(dict[str, EngineProcessor]):
self[eng_proc.engine.name] = eng_proc self[eng_proc.engine.name] = eng_proc
# logger.debug("registered engine processor: %s", eng_proc.engine.name) # logger.debug("registered engine processor: %s", eng_proc.engine.name)
else: else:
logger.error("can't register engine processor: %s (init failed)", eng_proc.engine.name) logger.error(
f"(PID {os.getpid()}) {eng_proc.engine.name}: can't register engines processor (init engine failed)"
)
return eng_proc_ok return eng_proc_ok

View File

@@ -150,19 +150,26 @@ class EngineProcessor(ABC):
threading.Thread(target=__init_processor_thread, daemon=True).start() threading.Thread(target=__init_processor_thread, daemon=True).start()
def init_engine(self) -> bool: def init_engine(self) -> bool:
eng_setting = get_engine_from_settings(self.engine.name) eng_setting = get_engine_from_settings(self.engine.name)
init_ok: bool | None = False init_ok: bool | None = False
try: try:
init_ok = self.engine.init(eng_setting) init_ok = self.engine.init(eng_setting)
except Exception: # pylint: disable=broad-except except Exception as e: # pylint: disable=broad-except
logger.exception( logger.exception(f"(PID {os.getpid()}) {self.engine.name}: engine INIT failed, exception: {e}")
f"(PID {os.getpid()}) Init method of engine %s failed due to an exception.", self.engine.name
)
init_ok = False init_ok = False
# In older engines, None is returned from the init method, which is # In older engines, None is returned from the init method, which is
# equivalent to indicating that the initialization was successful. # equivalent to indicating that the initialization was successful
# (compare: Engine.setup).
if init_ok is None: if init_ok is None:
init_ok = True init_ok = True
if not init_ok:
logger.error(f"(PID {os.getpid()}) {self.engine.name}: engine init was not successful")
return init_ok return init_ok
def handle_exception( def handle_exception(