[chore] complete and normalize the attributes of engine objects (#6258)

Drop outdated engine attributes: supported_languages, language_aliases

Complete, normalize and document the type definitions for the engine-module and
engine-class.

For the ``engine.about`` section of the configuration, a type check is performed
based on structure ``searx.enginelib.EngineAbout``.

The property ``engine.about.language`` no longer exists; existing values have
been migrated to ``engine.language``.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
This commit is contained in:
Markus Heiser
2026-06-14 15:16:26 +02:00
committed by Markus Heiser
parent b3e08f2a44
commit 6c9dcd4242
45 changed files with 210 additions and 159 deletions
+2 -2
View File
@@ -58,8 +58,8 @@ Configured Engines
{% for mod in engines %} {% for mod in engines %}
* - `{{mod.name}} <{{mod.about and mod.about.website}}>`_ * - `{{mod.name}} <{{mod.about and mod.about.website}}>`_
{%- if mod.about and mod.about.language %} {%- if mod.language %}
({{mod.about.language | upper}}) ({{mod.language | upper}})
{%- endif %} {%- endif %}
- ``!{{mod.shortcut}}`` - ``!{{mod.shortcut}}``
- {%- if 'searx.engines.' + mod.__name__ in documented_modules %} - {%- if 'searx.engines.' + mod.__name__ in documented_modules %}
+76 -63
View File
@@ -41,7 +41,7 @@ if t.TYPE_CHECKING:
from searx.enginelib.traits import EngineTraits from searx.enginelib.traits import EngineTraits
from searx.extended_types import SXNG_Response from searx.extended_types import SXNG_Response
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.search.processors import OfflineParamTypes, OnlineParamTypes from searx.search.processors import OfflineParamTypes, OnlineParamTypes, ProcessorType
ENGINES_CACHE: ExpireCacheSQLite = ExpireCacheSQLite.build_cache( ENGINES_CACHE: ExpireCacheSQLite = ExpireCacheSQLite.build_cache(
ExpireCacheCfg( ExpireCacheCfg(
@@ -180,7 +180,7 @@ class EngineCache:
return ENGINES_CACHE.secret_hash(name=name) return ENGINES_CACHE.secret_hash(name=name)
class EngineAbout(msgspec.Struct): class EngineAbout(msgspec.Struct, kw_only=True):
"""Additional fields describing the engine. """Additional fields describing the engine.
.. code:: yaml .. code:: yaml
@@ -213,12 +213,11 @@ class EngineAbout(msgspec.Struct):
results: str = "" results: str = ""
"""Data format of the source (online-engines: of the response).""" """Data format of the source (online-engines: of the response)."""
language: str = "" description: str = ""
"""If the engine supports only one language, this language is specified """Brief description of the engine and where it gets its data from.
here (``en``, ``de``, ``"no"`` or ..); otherwise, the value remains empty.
For the YAML configuration: think of the `YAML-Norway problem This value should only be set as long as no description of the data source
<https://ruuda.nl/2023/the-yaml-document-from-hell#the-norway-problem>`_ is available via a :py:obj:`EngineAbout.wikidata_id`.
""" """
@@ -227,6 +226,8 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
Further documentation see :ref:`general engine configuration`. Further documentation see :ref:`general engine configuration`.
The defaults are taken from :py:obj:`searx.engines.ENGINE_DEFAULT_ARGS`.
.. hint:: .. hint::
This class is currently never initialized and only used for type hinting. This class is currently never initialized and only used for type hinting.
@@ -234,49 +235,27 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
logger: logging.Logger logger: logging.Logger
# Common options in the engine module # Common options of the engine module
engine_type: str engine_type: "ProcessorType" = "online"
"""Type of the engine (:ref:`searx.search.processors`)""" """Type of the engine (:ref:`searx.search.processors`)"""
paging: bool paging: bool = False
"""Engine supports multiple pages.""" """Engine supports multiple pages."""
max_page: int = 0 max_page: int = 0
"""If the engine supports paging, then this is the value for the last page """If the engine supports paging, then this is the value for the last page
that is still supported. ``0`` means unlimited numbers of pages.""" that is still supported. ``0`` means unlimited numbers of pages."""
time_range_support: bool time_range_support: bool = False
"""Engine supports search time range.""" """Engine supports search time range."""
safesearch: bool safesearch: bool = False
"""Engine supports SafeSearch""" """Engine supports SafeSearch"""
language_support: bool language_support: bool = False
"""Engine supports languages (locales) search.""" """Engine supports languages (locales) search."""
language: str
"""For an engine, when there is ``language: ...`` in the YAML settings the engine
does support only this one language:
.. code:: yaml
- name: google french
engine: google
language: fr
"""
region: str
"""For an engine, when there is ``region: ...`` in the YAML settings the engine
does support only this one region::
.. code:: yaml
- name: google belgium
engine: google
region: fr-BE
"""
fetch_traits: "Callable[[EngineTraits, bool], None]" fetch_traits: "Callable[[EngineTraits, bool], None]"
"""Function to to fetch engine's traits from origin.""" """Function to to fetch engine's traits from origin."""
@@ -285,9 +264,6 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
# settings.yml # settings.yml
categories: list[str]
"""Specifies to which :ref:`engine categories` the engine should be added."""
name: str name: str
"""Name that will be used across SearXNG to define this engine. In settings, on """Name that will be used across SearXNG to define this engine. In settings, on
the result page ..""" the result page .."""
@@ -297,6 +273,43 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
this search engine (file name from :origin:`searx/engines` without this search engine (file name from :origin:`searx/engines` without
``.py``).""" ``.py``)."""
categories: list[str] = ["general"]
"""Specifies to which :ref:`engine categories` the engine should be added."""
language: str = ""
"""If the engine supports only one language, this language is specified here
(``en``, ``de``, ``"no"`` or ..); otherwise, the value remains empty. For
the YAML configuration: think of the `YAML-Norway problem
<https://ruuda.nl/2023/the-yaml-document-from-hell#the-norway-problem>`_
.. code:: yaml
- name: google norway
engine: google
language: "no"
Depending on ``language_support``, this value has similar but also slightly
different meanings.
- When ``language_support`` is **true**, the map of
:py:obj:`traits.EngineTraits.languages` is reduced to the selected
language
- When ``language_support`` is **false**, then the implementation of the
engine only supports this one ``language``
"""
region: str = ""
"""For an engine, when there is ``region: ...`` in the YAML settings the engine
does support only this one region::
.. code:: yaml
- name: google belgium
engine: google
region: fr-BE
"""
enable_http: bool enable_http: bool
"""Enable HTTP (by default only HTTPS is enabled).""" """Enable HTTP (by default only HTTPS is enabled)."""
@@ -309,6 +322,31 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
display_error_messages: bool display_error_messages: bool
"""Display error messages on the web UI.""" """Display error messages on the web UI."""
disabled: bool = False
"""To disable by default the engine, but not deleting it. It will allow the
user to manually activate it in the settings."""
inactive: bool = False
"""Remove the engine from the settings (*disabled & removed*)."""
about: EngineAbout = EngineAbout()
"""Additional fields describing the engine."""
using_tor_proxy: bool = False
"""Using tor proxy (``true``) or not (``false``) for this engine."""
send_accept_language_header: bool = True
"""When this option is activated (default), the language (locale) that is
selected by the user is used to build and send a ``Accept-Language`` header
in the request to the origin search engine."""
tokens: list[str] = []
"""A list of secret tokens to make this engine *private*, more details see
:ref:`private engines`."""
weight: float = 1.0
"""Weighting of the results of this engine (:ref:`weight <settings engines>`)."""
proxies: dict[str, dict[str, str]] proxies: dict[str, dict[str, str]]
"""Set proxies for a specific engine (YAML): """Set proxies for a specific engine (YAML):
@@ -319,31 +357,6 @@ class Engine(abc.ABC): # pylint: disable=too-few-public-methods
https: socks5://proxy:port https: socks5://proxy:port
""" """
disabled: bool
"""To disable by default the engine, but not deleting it. It will allow the
user to manually activate it in the settings."""
inactive: bool
"""Remove the engine from the settings (*disabled & removed*)."""
about: EngineAbout
"""Additional fields describing the engine."""
using_tor_proxy: bool
"""Using tor proxy (``true``) or not (``false``) for this engine."""
send_accept_language_header: bool
"""When this option is activated (default), the language (locale) that is
selected by the user is used to build and send a ``Accept-Language`` header
in the request to the origin search engine."""
tokens: list[str]
"""A list of secret tokens to make this engine *private*, more details see
:ref:`private engines`."""
weight: int
"""Weighting of the results of this engine (:ref:`weight <settings engines>`)."""
def setup(self, engine_settings: dict[str, t.Any]) -> bool: # pylint: disable=unused-argument def setup(self, engine_settings: dict[str, t.Any]) -> bool: # pylint: disable=unused-argument
"""Dynamic setup of the engine settings. """Dynamic setup of the engine settings.
+14 -11
View File
@@ -142,11 +142,11 @@ class EngineTraits:
""" """
if self.data_type == "traits_v1": if self.data_type == "traits_v1":
self._set_traits_v1(engine) self._set_traits_v1(engine) # pyright: ignore[reportArgumentType]
else: else:
raise TypeError("engine traits of type %s is unknown" % self.data_type) raise TypeError("engine traits of type %s is unknown" % self.data_type)
def _set_traits_v1(self, engine: "Engine | types.ModuleType") -> None: def _set_traits_v1(self, engine: "Engine") -> None:
# For an engine, when there is `language: ...` in the YAML settings the engine # For an engine, when there is `language: ...` in the YAML settings the engine
# does support only this one language (region):: # does support only this one language (region)::
# #
@@ -159,22 +159,25 @@ class EngineTraits:
_msg = "settings.yml - engine: '%s' / %s: '%s' not supported" _msg = "settings.yml - engine: '%s' / %s: '%s' not supported"
languages = traits.languages if engine.language:
if hasattr(engine, "language"): if engine.language_support:
if engine.language not in languages: if not len(traits.languages) > 1:
raise ValueError(
f"engine {engine.name}: activated language_support with just one or less languages"
)
if engine.language not in traits.languages:
raise ValueError(_msg % (engine.name, "language", engine.language)) raise ValueError(_msg % (engine.name, "language", engine.language))
traits.languages = {engine.language: languages[engine.language]} traits.languages = {engine.language: traits.languages[engine.language]}
regions = traits.regions if engine.region:
if hasattr(engine, "region"): if engine.region not in traits.regions:
if engine.region not in regions:
raise ValueError(_msg % (engine.name, "region", engine.region)) raise ValueError(_msg % (engine.name, "region", engine.region))
traits.regions = {engine.region: regions[engine.region]} traits.regions = {engine.region: traits.regions[engine.region]}
engine.language_support = bool(traits.languages or traits.regions) engine.language_support = bool(traits.languages or traits.regions)
# set the copied & modified traits in engine's namespace # set the copied & modified traits in engine's namespace
engine.traits = traits # pyright: ignore[reportAttributeAccessIssue] engine.traits = traits
class EngineTraitsMap(dict[str, EngineTraits]): class EngineTraitsMap(dict[str, EngineTraits]):
+1 -1
View File
@@ -22,8 +22,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "zh",
} }
language = "zh"
# Engine Configuration # Engine Configuration
categories = ["general"] categories = ["general"]
+6 -6
View File
@@ -5,19 +5,19 @@ intended monkey patching of the engine modules.
.. attention:: .. attention::
Monkey-patching modules is a practice from the past that shouldn't be Monkey-patching modules is a practice from the past that shouldn't be
expanded upon. In the long run, there should be an engine class that can be expanded upon. In the long run, engines should be instances of
inherited. However, as long as this class doesn't exist, and as long as all :py:obj:`searx.enginelib.Engine`. However, as long as long as all engine
engine modules aren't converted to an engine class, these builtin types will modules aren't converted to this class, these builtin types will still be
still be needed. needed.
""" """
import logging import logging
from searx.enginelib import traits as _traits from searx.enginelib import traits as _traits
logger: logging.Logger logger: logging.Logger
supported_languages: str
language_aliases: str
language_support: bool language_support: bool
language: str
region: str
traits: _traits.EngineTraits traits: _traits.EngineTraits
# from searx.engines.ENGINE_DEFAULT_ARGS # from searx.engines.ENGINE_DEFAULT_ARGS
+28 -7
View File
@@ -17,37 +17,44 @@ from os.path import realpath, dirname
import types import types
import inspect import inspect
import msgspec
from searx import logger, settings from searx import logger, settings
from searx.utils import load_module from searx.utils import load_module
if t.TYPE_CHECKING: from searx.enginelib import Engine, EngineAbout
from searx.enginelib import Engine
logger = logger.getChild('engines') logger = logger.getChild('engines')
ENGINE_DIR = dirname(realpath(__file__)) ENGINE_DIR = dirname(realpath(__file__))
# Defaults for the namespace of an engine module, see load_engine() # Defaults for the namespace of an engine module, see load_engine()
ENGINE_DEFAULT_ARGS: dict[str, int | str | list[t.Any] | dict[str, t.Any] | bool] = { ENGINE_DEFAULT_ARGS: dict[str, t.Any] = {
# Common options in the engine module # Common options in the engine module
"engine_type": "online", "engine_type": "online",
"paging": False, "paging": False,
"max_page": 0,
"time_range_support": False, "time_range_support": False,
"safesearch": False, "safesearch": False,
"language_support": False,
# settings.yml # settings.yml
"categories": ["general"], "categories": ["general"],
"language": "",
"region": "",
"enable_http": False, "enable_http": False,
"shortcut": "-", "shortcut": "-",
"timeout": settings["outgoing"]["request_timeout"], "timeout": settings["outgoing"]["request_timeout"],
"display_error_messages": True, "display_error_messages": True,
"disabled": False, "disabled": False,
"inactive": False, "inactive": False,
"about": {}, "about": EngineAbout(),
"using_tor_proxy": False, "using_tor_proxy": False,
"send_accept_language_header": True, "send_accept_language_header": True,
"tokens": [], "tokens": [],
"max_page": 0, "weight": 1.0,
} }
"""Default values that are set in an engine of type *module*, please compare
with the class :py:obj:`searx.enginelib.Engine`."""
# set automatically when an engine does not have any tab category # set automatically when an engine does not have any tab category
DEFAULT_CATEGORY = 'other' DEFAULT_CATEGORY = 'other'
@@ -178,13 +185,27 @@ def set_loggers(engine: "Engine|types.ModuleType", engine_name: str):
def update_engine_attributes(engine: "Engine | types.ModuleType", engine_data: dict[str, t.Any]): def update_engine_attributes(engine: "Engine | types.ModuleType", engine_data: dict[str, t.Any]):
# set engine attributes from engine_data # set engine attributes from engine_data
kvargs: dict[str, t.Any]
if isinstance(engine.about, EngineAbout):
kvargs = {**msgspec.to_builtins(engine.about), **engine_data.get("about", {})}
else:
kvargs = {**engine.about, **engine_data.get("about", {})}
try:
engine.about = EngineAbout(**kvargs)
except TypeError as exc:
raise TypeError(
f"engine {engine_data['name']} ({engine_data['engine']}) - in the about section --> {exc}"
) from exc
for param_name, param_value in engine_data.items(): for param_name, param_value in engine_data.items():
if param_name == "about":
continue
if param_name == 'categories': if param_name == 'categories':
if isinstance(param_value, str): if isinstance(param_value, str):
param_value = list(map(str.strip, param_value.split(','))) param_value = list(map(str.strip, param_value.split(',')))
engine.categories = param_value # type: ignore engine.categories = param_value # type: ignore
elif hasattr(engine, 'about') and param_name == 'about':
engine.about = {**engine.about, **engine_data['about']} # type: ignore
else: else:
setattr(engine, param_name, param_value) setattr(engine, param_name, param_value)
+1 -1
View File
@@ -16,12 +16,12 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "zh",
} }
# Engine Configuration # Engine Configuration
categories = ["videos"] categories = ["videos"]
paging = True paging = True
language = "zh"
# Base URL # Base URL
base_url = "https://www.acfun.cn" base_url = "https://www.acfun.cn"
+1 -1
View File
@@ -42,8 +42,8 @@ about = {
'use_official_api': False, 'use_official_api': False,
'require_api_key': False, 'require_api_key': False,
'results': 'HTML', 'results': 'HTML',
'language': 'it',
} }
language = "it"
def request(query, params): def request(query, params):
+1 -1
View File
@@ -54,8 +54,8 @@ about = {
"use_official_api": True, "use_official_api": True,
"require_api_key": True, "require_api_key": True,
"results": "JSON", "results": "JSON",
"language": "en",
} }
language = "en"
CACHE: EngineCache CACHE: EngineCache
"""Persistent (SQLite) key/value cache that deletes its values after ``expire`` """Persistent (SQLite) key/value cache that deletes its values after ``expire``
+1 -1
View File
@@ -23,8 +23,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "JSON", "results": "JSON",
"language": "zh",
} }
language = "zh"
paging = True paging = True
categories = [] categories = []
+1 -1
View File
@@ -13,8 +13,8 @@ about = {
'use_official_api': False, 'use_official_api': False,
'require_api_key': False, 'require_api_key': False,
'results': 'JSON', 'results': 'JSON',
'language': 'de',
} }
language = "de"
paging = True paging = True
categories = ['general'] categories = ['general']
+1 -1
View File
@@ -10,8 +10,8 @@ about = {
'use_official_api': False, 'use_official_api': False,
'require_api_key': False, 'require_api_key': False,
'results': 'JSON', 'results': 'JSON',
'language': 'de',
} }
language = "de"
paging = True paging = True
categories = [] categories = []
+1 -1
View File
@@ -70,13 +70,13 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "JSON", "results": "JSON",
"language": "zh",
} }
paging = True paging = True
time_range_support = True time_range_support = True
results_per_page = 10 results_per_page = 10
categories = [] categories = []
language = "zh"
ChinasoCategoryType = t.Literal['news', 'videos', 'images'] ChinasoCategoryType = t.Literal['news', 'videos', 'images']
"""ChinaSo supports news, videos, images search. """ChinaSo supports news, videos, images search.
+6 -8
View File
@@ -24,7 +24,7 @@ import typing as t
import json import json
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.enginelib import EngineCache from searx.enginelib import EngineCache, EngineAbout
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from searx.search.processors import RequestParams from searx.search.processors import RequestParams
@@ -35,13 +35,11 @@ categories = ["general"]
disabled = True disabled = True
timeout = 2.0 timeout = 2.0
about = { language = "en"
"wikidata_id": None, about = EngineAbout(
"official_api_documentation": None, results="JSON",
"use_official_api": False, description="Demo offline engine Engine with results in the English language.",
"require_api_key": False, )
"results": "JSON",
}
# if there is a need for globals, use a leading underline # if there is a need for globals, use a leading underline
_my_offline_engine: str = "" _my_offline_engine: str = ""
+9 -8
View File
@@ -25,6 +25,7 @@ import typing as t
from urllib.parse import urlencode from urllib.parse import urlencode
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.enginelib import EngineAbout
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response from searx.extended_types import SXNG_Response
@@ -43,14 +44,14 @@ page_size = 20
search_api = "https://api.artic.edu/api/v1/artworks/search" search_api = "https://api.artic.edu/api/v1/artworks/search"
image_api = "https://www.artic.edu/iiif/2/" image_api = "https://www.artic.edu/iiif/2/"
about = { about = EngineAbout(
"website": "https://www.artic.edu", website="https://www.artic.edu",
"wikidata_id": "Q239303", wikidata_id="Q239303",
"official_api_documentation": "http://api.artic.edu/docs/", official_api_documentation="http://api.artic.edu/docs/",
"use_official_api": True, use_official_api=True,
"require_api_key": False, require_api_key=False,
"results": "JSON", results="JSON",
} )
# if there is a need for globals, use a leading underline # if there is a need for globals, use a leading underline
+1 -1
View File
@@ -11,8 +11,8 @@ about = {
'use_official_api': False, 'use_official_api': False,
'require_api_key': False, 'require_api_key': False,
'results': 'HTML', 'results': 'HTML',
'language': 'de',
} }
language = "de"
categories = [] categories = []
paging = True paging = True
+1 -1
View File
@@ -14,8 +14,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": 'HTML', "results": 'HTML',
"language": 'de',
} }
language = "de"
categories = ['dictionaries'] categories = ['dictionaries']
paging = True paging = True
+1 -1
View File
@@ -55,7 +55,7 @@ about = {
'official_api_documentation': 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html', 'official_api_documentation': 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html',
'use_official_api': True, 'use_official_api': True,
'require_api_key': False, 'require_api_key': False,
'format': 'JSON', "results": "JSON",
} }
base_url = 'http://localhost:9200' base_url = 'http://localhost:9200'
+1 -1
View File
@@ -27,8 +27,8 @@ about = {
'official_api_documentation': None, 'official_api_documentation': None,
'require_api_key': False, 'require_api_key': False,
'results': 'HTML', 'results': 'HTML',
'language': 'de',
} }
language = "de"
paging = True paging = True
categories = ['shopping'] categories = ['shopping']
+1 -1
View File
@@ -34,8 +34,8 @@ about = {
"use_official_api": True, "use_official_api": True,
"require_api_key": False, "require_api_key": False,
"results": "JSON", "results": "JSON",
"language": "it",
} }
language = "it"
def request(query, params): def request(query, params):
+1 -1
View File
@@ -16,8 +16,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": 'HTML', "results": 'HTML',
"language": 'fr',
} }
language = "fr"
# engine dependent config # engine dependent config
categories = ['videos'] categories = ['videos']
+1 -1
View File
@@ -14,9 +14,9 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "JSON", "results": "JSON",
"language": "zh",
} }
language = "zh"
paging = True paging = True
time_range_support = True time_range_support = True
categories = ["videos"] categories = ["videos"]
+1 -1
View File
@@ -13,8 +13,8 @@ about = {
"use_official_api": True, "use_official_api": True,
"require_api_key": False, "require_api_key": False,
"results": 'JSON', "results": 'JSON',
"language": 'ja',
} }
language = "ja"
categories = ['dictionaries'] categories = ['dictionaries']
paging = False paging = False
+3
View File
@@ -79,6 +79,9 @@ from json import loads
from urllib.parse import urlencode from urllib.parse import urlencode
from searx.utils import to_string, html_to_text from searx.utils import to_string, html_to_text
from searx.network import raise_for_httperror from searx.network import raise_for_httperror
from searx.enginelib import EngineAbout
about = EngineAbout()
search_url = None search_url = None
""" """
+1 -1
View File
@@ -11,9 +11,9 @@ about = {
"use_official_api": True, "use_official_api": True,
"require_api_key": False, "require_api_key": False,
"results": 'JSON', "results": 'JSON',
"language": "de",
} }
language = "de"
categories = ['videos'] categories = ['videos']
paging = True paging = True
time_range_support = False time_range_support = False
+2 -1
View File
@@ -35,8 +35,9 @@ about = {
'use_official_api': False, 'use_official_api': False,
'require_api_key': False, 'require_api_key': False,
'results': 'JSON', 'results': 'JSON',
'language': 'de',
} }
language = "de"
paging = True paging = True
categories = ["movies"] categories = ["movies"]
+1 -1
View File
@@ -26,8 +26,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "ko",
} }
language = "ko"
categories = [] categories = []
paging = True paging = True
+1 -1
View File
@@ -13,8 +13,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "ja",
} }
language = "ja"
categories = ["videos"] categories = ["videos"]
paging = True paging = True
+2 -2
View File
@@ -28,7 +28,7 @@ search_string = 'api/?{query}&limit={limit}'
result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}' result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}'
# list of supported languages # list of supported languages
supported_languages = ['de', 'en', 'fr', 'it'] photon_supported_languages = ["de", "en", "fr", "it"]
# do search-request # do search-request
@@ -37,7 +37,7 @@ def request(query, params):
if params['language'] != 'all': if params['language'] != 'all':
language = params['language'].split('_')[0] language = params['language'].split('_')[0]
if language in supported_languages: if language in photon_supported_languages:
params['url'] = params['url'] + "&lang=" + language params['url'] = params['url'] + "&lang=" + language
# using SearXNG User-Agent # using SearXNG User-Agent
+1 -1
View File
@@ -77,7 +77,7 @@ from searx.utils import gen_useragent, html_to_text, parse_duration_string
about = { about = {
"website": "https://presearch.io", "website": "https://presearch.io",
"wikidiata_id": "Q7240905", "wikidata_id": "Q7240905",
"official_api_documentation": "https://docs.presearch.io/nodes/api", "official_api_documentation": "https://docs.presearch.io/nodes/api",
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
+1 -1
View File
@@ -16,8 +16,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "zh",
} }
language = "zh"
# Engine Configuration # Engine Configuration
categories = [] categories = []
+1 -1
View File
@@ -13,8 +13,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": 'JSON', "results": 'JSON',
'language': 'fr',
} }
language = "fr"
categories = ['movies'] categories = ['movies']
paging = True paging = True
+1 -1
View File
@@ -19,8 +19,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "cz",
} }
language = "cz"
categories = ['general', 'web'] categories = ['general', 'web']
base_url = 'https://search.seznam.cz/' base_url = 'https://search.seznam.cz/'
+1 -1
View File
@@ -16,8 +16,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "zh",
} }
language = "zh"
# Engine Configuration # Engine Configuration
categories = ["general"] categories = ["general"]
+1 -1
View File
@@ -11,8 +11,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "JSON", "results": "JSON",
"language": "zh",
} }
language = "zh"
categories = ["videos"] categories = ["videos"]
paging = True paging = True
+1 -1
View File
@@ -14,8 +14,8 @@ about = {
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": "HTML", "results": "HTML",
"language": "zh",
} }
language = "zh"
# Engine Configuration # Engine Configuration
categories = ["news"] categories = ["news"]
+2 -1
View File
@@ -27,8 +27,9 @@ about = {
'use_official_api': True, 'use_official_api': True,
'require_api_key': False, 'require_api_key': False,
'results': 'JSON', 'results': 'JSON',
'language': 'de',
} }
language = "de"
categories = ['general', 'news'] categories = ['general', 'news']
paging = True paging = True
+8 -9
View File
@@ -16,20 +16,17 @@ from lxml import html
from searx.utils import eval_xpath_list, eval_xpath, extract_text, get_embeded_stream_url, ElementType from searx.utils import eval_xpath_list, eval_xpath, extract_text, get_embeded_stream_url, ElementType
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.enginelib import EngineAbout
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineParams from searx.search.processors import OnlineParams
about = { about = EngineAbout(
"website": "https://www.t-online.de", website="https://www.t-online.de",
"wikidata_id": "Q590940", wikidata_id="Q590940",
"official_api_documentation": None, results="HTML",
"use_official_api": False, )
"require_api_key": False,
"results": "HTML",
"language": "de",
}
paging = True paging = True
time_range_support = True time_range_support = True
@@ -44,6 +41,8 @@ time_range_map = {"day": "d", "week": "w", "month": "m", "year": "y"}
# use "tonline" to only search for results from t-online news articles # use "tonline" to only search for results from t-online news articles
tonline_channel_map = {"images": "flickr", "videos": "yt"} tonline_channel_map = {"images": "flickr", "videos": "yt"}
language = "de"
def init(_): def init(_):
if tonline_categ not in ("web", "images", "videos", "news"): if tonline_categ not in ("web", "images", "videos", "news"):
+3
View File
@@ -76,6 +76,9 @@ from lxml import html
from searx.utils import extract_text, extract_url, eval_xpath, eval_xpath_list from searx.utils import extract_text, extract_url, eval_xpath, eval_xpath_list
from searx.network import raise_for_httperror from searx.network import raise_for_httperror
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.enginelib import EngineAbout
about = EngineAbout()
search_url = None search_url = None
""" """
+9
View File
@@ -11,6 +11,7 @@ __all__ = [
"PROCESSORS", "PROCESSORS",
"ParamTypes", "ParamTypes",
"RequestParams", "RequestParams",
"ProcessorType",
] ]
import typing as t import typing as t
@@ -27,6 +28,14 @@ from .online_url_search import OnlineUrlSearchProcessor, OnlineUrlSearchParams
logger = logger.getChild("search.processors") logger = logger.getChild("search.processors")
ProcessorType = t.Literal[
"offline",
"online",
"online_currency",
"online_dictionary",
"online_url_search",
]
OnlineParamTypes: t.TypeAlias = OnlineParams | OnlineDictParams | OnlineCurrenciesParams | OnlineUrlSearchParams OnlineParamTypes: t.TypeAlias = OnlineParams | OnlineDictParams | OnlineCurrenciesParams | OnlineUrlSearchParams
OfflineParamTypes: t.TypeAlias = RequestParams OfflineParamTypes: t.TypeAlias = RequestParams
ParamTypes: t.TypeAlias = OfflineParamTypes | OnlineParamTypes ParamTypes: t.TypeAlias = OfflineParamTypes | OnlineParamTypes
+4 -4
View File
@@ -330,12 +330,12 @@ engines:
url_xpath: ./a/@href url_xpath: ./a/@href
title_xpath: ./a/h3 title_xpath: ./a/h3
content_xpath: ./div content_xpath: ./div
language: "no"
about: about:
website: https://abcnyheter.no website: https://abcnyheter.no
use_official_api: false use_official_api: false
require_api_key: false require_api_key: false
results: HTML results: HTML
language: "no"
- name: acfun - name: acfun
engine: acfun engine: acfun
@@ -2808,13 +2808,13 @@ engines:
shortcut: rel shortcut: rel
categories: general categories: general
disabled: true disabled: true
language: de
about: about:
website: https://reloado.com website: https://reloado.com
official_api_documentation: official_api_documentation:
use_official_api: false use_official_api: false
require_api_key: false require_api_key: false
results: HTML results: HTML
language: de
- name: repology - name: repology
engine: repology engine: repology
@@ -2927,13 +2927,13 @@ engines:
content_xpath: //div[@class="synonyms-list-group"] content_xpath: //div[@class="synonyms-list-group"]
title_xpath: //div[@class="upper-synonyms"]/a title_xpath: //div[@class="upper-synonyms"]/a
no_result_for_http_status: [404] no_result_for_http_status: [404]
language: de
about: about:
website: https://www.woxikon.de/ website: https://www.woxikon.de/
wikidata_id: # No Wikidata ID wikidata_id: # No Wikidata ID
use_official_api: false use_official_api: false
require_api_key: false require_api_key: false
results: HTML results: HTML
language: de
- name: tootfinder - name: tootfinder
engine: tootfinder engine: tootfinder
@@ -2988,13 +2988,13 @@ engines:
content_xpath: //li/div[@class="searchresult"] content_xpath: //li/div[@class="searchresult"]
categories: general categories: general
disabled: true disabled: true
language: fr
about: about:
website: https://wikimini.org/ website: https://wikimini.org/
wikidata_id: Q3568032 wikidata_id: Q3568032
use_official_api: false use_official_api: false
require_api_key: false require_api_key: false
results: HTML results: HTML
language: fr
- name: wttr.in - name: wttr.in
engine: wttr engine: wttr
@@ -60,8 +60,8 @@
{%- endif -%} {%- endif -%}
<label for="{{ engine_id }}"> <label for="{{ engine_id }}">
{{- ' ' -}}{{- search_engine.name -}} {{- ' ' -}}{{- search_engine.name -}}
{%- if search_engine.about and search_engine.about.language -%} {%- if search_engine.language -%}
{{- ' ' -}}({{search_engine.about.language | upper}}) {{- ' ' -}}({{search_engine.language | upper}})
{%- endif -%} {%- endif -%}
</label> </label>
{{- engine_about(search_engine) -}} {{- engine_about(search_engine) -}}
+3 -4
View File
@@ -1075,10 +1075,9 @@ def engine_descriptions():
result[engine] = description result[engine] = description
# overwrite by about:description (from settings) # overwrite by about:description (from settings)
for engine_name, engine_mod in engines.items(): for eng_name, eng_obj in engines.items():
descr = getattr(engine_mod, 'about', {}).get('description', None) if eng_obj.about.description:
if descr is not None: result[eng_name] = [eng_obj.about.description, "SearXNG config"]
result[engine_name] = [descr, "SearXNG config"]
return jsonify(result) return jsonify(result)
+1 -1
View File
@@ -323,7 +323,7 @@ def group_engines_in_tab(engines: "Iterable[Engine]") -> List[Tuple[str, "Iterab
return (group[0] == NO_SUBGROUPING, group[0].lower()) return (group[0] == NO_SUBGROUPING, group[0].lower())
def engine_sort_key(engine): def engine_sort_key(engine):
return (engine.about.get('language', ''), engine.name) return (engine.language, engine.name)
tabs = list(get_setting('categories_as_tabs').keys()) tabs = list(get_setting('categories_as_tabs').keys())
subgroups = itertools.groupby(sorted(engines, key=get_subgroup), get_subgroup) subgroups = itertools.groupby(sorted(engines, key=get_subgroup), get_subgroup)
+1 -1
View File
@@ -36,7 +36,7 @@ test.pylint() {
build_msg TEST "[pylint] ./searx/engines" build_msg TEST "[pylint] ./searx/engines"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
pylint ${PYLINT_OPTIONS} ${PYLINT_VERBOSE} \ pylint ${PYLINT_OPTIONS} ${PYLINT_VERBOSE} \
--additional-builtins="traits,supported_languages,language_aliases,logger,categories" \ --additional-builtins="traits,logger,categories" \
searx/engines searx/engines
build_msg TEST "[pylint] ./searx ./searxng_extra ./tests" build_msg TEST "[pylint] ./searx ./searxng_extra ./tests"