2"""Load and initialize the ``engines``, see :py:func:`load_engines` and register
3:py:obj:`engine_shortcuts`.
7 load_engines( settings['engines'] )
11from __future__
import annotations
15from os.path
import realpath, dirname
17from typing
import TYPE_CHECKING, Dict
21from searx
import logger, settings
27logger = logger.getChild(
'engines')
28ENGINE_DIR = dirname(realpath(__file__))
29ENGINE_DEFAULT_ARGS = {
31 "engine_type":
"online",
33 "time_range_support":
False,
36 "categories": [
"general"],
39 "timeout": settings[
"outgoing"][
"request_timeout"],
40 "display_error_messages":
True,
44 "using_tor_proxy":
False,
45 "send_accept_language_header":
False,
50DEFAULT_CATEGORY =
'other'
55categories = {
'general': []}
56engines: Dict[str, Engine | types.ModuleType] = {}
58"""Simple map of registered *shortcuts* to name of the engine (or ``None``).
62 engine_shortcuts[engine.shortcut] = engine.name
73 obj = getattr(module,
'network',
None)
74 if obj
and inspect.ismodule(obj):
75 msg = f
'type of {module.__name__}.network is a module ({obj.__name__}), expected a string'
80def load_engine(engine_data: dict) -> Engine | types.ModuleType |
None:
81 """Load engine from ``engine_data``.
83 :param dict engine_data: Attributes from YAML ``settings:engines/<engine>``
84 :return: initialized namespace of the ``<engine>``.
86 1. create a namespace and load module of the ``<engine>``
87 2. update namespace with the defaults from :py:obj:`ENGINE_DEFAULT_ARGS`
88 3. update namespace with values from ``engine_data``
90 If engine *is active*, return namespace of the engine, otherwise return
93 This function also returns ``None`` if initialization of the namespace fails
94 for one of the following reasons:
96 - engine name contains underscore
97 - engine name is not lowercase
98 - required attribute is not set :py:func:`is_missing_required_attributes`
103 engine_name = engine_data.get(
'name')
104 if engine_name
is None:
105 logger.error(
'An engine does not have a "name" field')
107 if '_' in engine_name:
108 logger.error(
'Engine name contains underscore: "{}"'.
format(engine_name))
111 if engine_name.lower() != engine_name:
112 logger.warning(
'Engine name is not lowercase: "{}", converting to lowercase'.
format(engine_name))
113 engine_name = engine_name.lower()
114 engine_data[
'name'] = engine_name
117 module_name = engine_data.get(
'engine')
118 if module_name
is None:
119 logger.error(
'The "engine" field is missing for the engine named "{}"'.
format(engine_name))
122 engine = load_module(module_name +
'.py', ENGINE_DIR)
123 except (SyntaxError, KeyboardInterrupt, SystemExit, SystemError, ImportError, RuntimeError):
124 logger.exception(
'Fatal exception in engine "{}"'.
format(module_name))
126 except BaseException:
127 logger.exception(
'Cannot load engine "{}"'.
format(module_name))
138 trait_map = EngineTraitsMap.from_data()
139 trait_map.set_traits(engine)
149 if not any(cat
in settings[
'categories_as_tabs']
for cat
in engine.categories):
150 engine.categories.append(DEFAULT_CATEGORY)
157 engine.logger = logger.getChild(engine_name)
163 modules = sys.modules.copy()
164 for module_name, module
in modules.items():
166 module_name.startswith(
"searx.engines")
167 and module_name !=
"searx.engines.__init__"
168 and not hasattr(module,
"logger")
170 module_engine_name = module_name.split(
".")[-1]
171 module.logger = logger.getChild(module_engine_name)
176 for param_name, param_value
in engine_data.items():
177 if param_name ==
'categories':
178 if isinstance(param_value, str):
179 param_value = list(map(str.strip, param_value.split(
',')))
180 engine.categories = param_value
181 elif hasattr(engine,
'about')
and param_name ==
'about':
182 engine.about = {**engine.about, **engine_data[
'about']}
184 setattr(engine, param_name, param_value)
187 for arg_name, arg_value
in ENGINE_DEFAULT_ARGS.items():
188 if not hasattr(engine, arg_name):
189 setattr(engine, arg_name, copy.deepcopy(arg_value))
194 engine.search_url = engine.onion_url + getattr(engine,
'search_path',
'')
195 engine.timeout += settings[
'outgoing'].get(
'extra_proxy_timeout', 0)
199 """An attribute is required when its name doesn't start with ``_`` (underline).
200 Required attributes must not be ``None``.
204 for engine_attr
in dir(engine):
205 if not engine_attr.startswith(
'_')
and getattr(engine, engine_attr)
is None:
206 logger.error(
'Missing engine config attribute: "{0}.{1}"'.
format(engine.name, engine_attr))
212 """Return True if the engine configuration declares to use Tor."""
213 return settings[
'outgoing'].get(
'using_tor_proxy')
or getattr(engine,
'using_tor_proxy',
False)
218 if engine.inactive
is True:
229 if engine.name
in engines:
230 logger.error(
'Engine config error: ambiguous name: {0}'.
format(engine.name))
232 engines[engine.name] = engine
234 if engine.shortcut
in engine_shortcuts:
235 logger.error(
'Engine config error: ambiguous shortcut: {0}'.
format(engine.shortcut))
237 engine_shortcuts[engine.shortcut] = engine.name
239 for category_name
in engine.categories:
240 categories.setdefault(category_name, []).append(engine)
244 """usage: ``engine_list = settings['engines']``"""
246 engine_shortcuts.clear()
248 categories[
'general'] = []
249 for engine_data
in engine_list:
register_engine(Engine|types.ModuleType engine)
update_attributes_for_tor(Engine|types.ModuleType engine)
using_tor_proxy(Engine|types.ModuleType engine)
Engine|types.ModuleType|None load_engine(dict engine_data)
is_missing_required_attributes(engine)
check_engine_module(types.ModuleType module)
load_engines(engine_list)
is_engine_active(Engine|types.ModuleType engine)
update_engine_attributes(Engine|types.ModuleType engine, engine_data)
set_loggers(engine, engine_name)