[refactor] typification of SearXNG / EngineResults

In [1] and [2] we discussed the need of a Result.results property and how we can
avoid unclear code.  This patch implements a class for the reslut-lists of
engines::

    searx.result_types.EngineResults

A simple example for the usage in engine development::

    from searx.result_types import EngineResults
    ...
    def response(resp) -> EngineResults:
        res = EngineResults()
        ...
        res.add( res.types.Answer(answer="lorem ipsum ..", url="https://example.org") )
        ...
        return res

[1] https://github.com/searxng/searxng/pull/4183#pullrequestreview-257400034
[2] https://github.com/searxng/searxng/pull/4183#issuecomment-2614301580
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
This commit is contained in:
Markus Heiser 2025-01-27 16:43:43 +01:00 committed by Markus Heiser
parent edfbf1e118
commit 36a1ef1239
26 changed files with 195 additions and 140 deletions

View file

@ -9,10 +9,50 @@
gradually. For more, please read :ref:`result types`.
"""
# pylint: disable=too-few-public-methods
from __future__ import annotations
__all__ = ["Result", "AnswerSet", "Answer", "Translations"]
__all__ = ["Result", "EngineResults", "AnswerSet", "Answer", "Translations"]
import abc
from searx import enginelib
from ._base import Result, LegacyResult
from .answer import AnswerSet, Answer, Translations
class ResultList(list, abc.ABC):
"""Base class of all result lists (abstract)."""
class types: # pylint: disable=invalid-name
"""The collection of result types (which have already been implemented)."""
Answer = Answer
Translations = Translations
def __init__(self):
# pylint: disable=useless-parent-delegation
super().__init__()
def add(self, result: Result):
"""Add a :py:`Result` item to the result list."""
self.append(result)
class EngineResults(ResultList):
"""Result list that should be used by engine developers. For convenience,
engine developers don't need to import types / see :py:obj:`ResultList.types`.
.. code:: python
from searx.result_types import EngineResults
...
def response(resp) -> EngineResults:
res = EngineResults()
...
res.add( res.types.Answer(answer="lorem ipsum ..", url="https://example.org") )
...
return res
"""

View file

@ -53,27 +53,6 @@ class Result(msgspec.Struct, kw_only=True):
The field is optional and is initialized from the context if necessary.
"""
results: list = [] # https://jcristharif.com/msgspec/structs.html#default-values
"""Result list of an :origin:`engine <searx/engines>` response or a
:origin:`answerer <searx/answerers>` to which the answer should be added.
This field is only present for the sake of simplicity. Typically, the
response function of an engine has a result list that is returned at the
end. By specifying the result list in the constructor of the result, this
result is then immediately added to the list (this parameter does not have
another function).
.. code:: python
def response(resp):
results = []
...
Answer(results=results, answer=answer, url=url)
...
return results
"""
def normalize_result_fields(self):
"""Normalize a result ..
@ -92,9 +71,7 @@ class Result(msgspec.Struct, kw_only=True):
self.url = self.parsed_url.geturl()
def __post_init__(self):
"""Add *this* result to the result list."""
self.results.append(self)
pass
def __hash__(self) -> int:
"""Generates a hash value that uniquely identifies the content of *this*