2"""Engine's traits are fetched from the origin engines and stored in a JSON file
3in the *data folder*. Most often traits are languages and region codes and
4their mapping from SearXNG's representation to the representation in the origin
5search engine. For new traits new properties can be added to the class
6:py:class:`EngineTraits`.
8To load traits from the persistence :py:obj:`EngineTraitsMap.from_data` can be
12from __future__
import annotations
16from typing
import Dict, Literal, Iterable, Union, Callable, Optional, TYPE_CHECKING
18from searx
import locales
26 """Encodes :class:`EngineTraits` to a serializable object, see
27 :class:`json.JSONEncoder`."""
30 """Return dictionary of a :class:`EngineTraits` object."""
31 if isinstance(o, EngineTraits):
38 """The class is intended to be instantiated for each engine."""
40 regions: Dict[str, str] = dataclasses.field(default_factory=dict)
41 """Maps SearXNG's internal representation of a region to the one of the engine.
43 SearXNG's internal representation can be parsed by babel and the value is
49 'fr-BE' : <engine's region name>,
52 for key, egnine_region regions.items():
53 searxng_region = babel.Locale.parse(key, sep='-')
57 languages: Dict[str, str] = dataclasses.field(default_factory=dict)
58 """Maps SearXNG's internal representation of a language to the one of the engine.
60 SearXNG's internal representation can be parsed by babel and the value is
66 'ca' : <engine's language name>,
69 for key, egnine_lang in languages.items():
70 searxng_lang = babel.Locale.parse(key)
74 all_locale: Optional[str] =
None
75 """To which locale value SearXNG's ``all`` language is mapped (shown a "Default
79 data_type: Literal[
'traits_v1'] =
'traits_v1'
80 """Data type, default is 'traits_v1'.
83 custom: Dict[str, Union[Dict[str, Dict], Iterable[str]]] = dataclasses.field(default_factory=dict)
84 """A place to store engine's custom traits, not related to the SearXNG core.
88 """Return engine's language string that *best fits* to SearXNG's locale.
90 :param searxng_locale: SearXNG's internal representation of locale
93 :param default: engine's default language
95 The *best fits* rules are implemented in
96 :py:obj:`searx.locales.get_engine_locale`. Except for the special value ``all``
97 which is determined from :py:obj:`EngineTraits.all_locale`.
99 if searxng_locale ==
'all' and self.
all_locale is not None:
101 return locales.get_engine_locale(searxng_locale, self.
languages, default=default)
104 """Return engine's region string that best fits to SearXNG's locale.
106 :param searxng_locale: SearXNG's internal representation of locale
107 selected by the user.
109 :param default: engine's default region
111 The *best fits* rules are implemented in
112 :py:obj:`searx.locales.get_engine_locale`. Except for the special value ``all``
113 which is determined from :py:obj:`EngineTraits.all_locale`.
115 if searxng_locale ==
'all' and self.
all_locale is not None:
117 return locales.get_engine_locale(searxng_locale, self.
regions, default=default)
120 """A *locale* (SearXNG's internal representation) is considered to be
121 supported by the engine if the *region* or the *language* is supported
124 For verification the functions :py:func:`EngineTraits.get_region` and
125 :py:func:`EngineTraits.get_language` are used.
133 """Create a copy of the dataclass object."""
138 """Call a function ``fetch_traits(engine_traits)`` from engines namespace to fetch
139 and set properties from the origin engine in the object ``engine_traits``. If
140 function does not exists, ``None`` is returned.
143 fetch_traits = getattr(engine,
'fetch_traits',
None)
147 engine_traits = cls()
148 fetch_traits(engine_traits)
152 """Set traits from self object in a :py:obj:`.Engine` namespace.
154 :param engine: engine instance build by :py:func:`searx.engines.load_engine`
173 _msg =
"settings.yml - engine: '%s' / %s: '%s' not supported"
175 languages = traits.languages
176 if hasattr(engine,
'language'):
177 if engine.language
not in languages:
178 raise ValueError(_msg % (engine.name,
'language', engine.language))
179 traits.languages = {engine.language: languages[engine.language]}
181 regions = traits.regions
182 if hasattr(engine,
'region'):
183 if engine.region
not in regions:
184 raise ValueError(_msg % (engine.name,
'region', engine.region))
185 traits.regions = {engine.region: regions[engine.region]}
187 engine.language_support = bool(traits.languages
or traits.regions)
190 engine.traits = traits
194 """A python dictionary to map :class:`EngineTraits` by engine name."""
196 ENGINE_TRAITS_FILE = (data_dir /
'engine_traits.json').resolve()
197 """File with persistence of the :py:obj:`EngineTraitsMap`."""
200 """Store EngineTraitsMap in in file :py:obj:`self.ENGINE_TRAITS_FILE`"""
202 json.dump(self, f, indent=2, sort_keys=
True, cls=EngineTraitsEncoder)
206 """Instantiate :class:`EngineTraitsMap` object from :py:obj:`ENGINE_TRAITS`"""
208 for k, v
in ENGINE_TRAITS.items():
214 from searx
import engines
216 names = list(engines.engines)
220 for engine_name
in names:
221 engine = engines.engines[engine_name]
223 traits = EngineTraits.fetch_traits(engine)
224 if traits
is not None:
225 log(
"%-20s: SearXNG languages --> %s " % (engine_name, len(traits.languages)))
226 log(
"%-20s: SearXNG regions --> %s" % (engine_name, len(traits.regions)))
227 obj[engine_name] = traits
232 """Set traits in a :py:obj:`Engine` namespace.
234 :param engine: engine instance build by :py:func:`searx.engines.load_engine`
238 if engine.name
in self.keys():
239 engine_traits = self[engine.name]
241 elif engine.engine
in self.keys():
248 engine_traits = self[engine.engine]
250 engine_traits.set_traits(engine)
'EngineTraitsMap' fetch_traits(cls, Callable log)
'EngineTraitsMap' from_data(cls)
set_traits(self, Engine|types.ModuleType engine)
_set_traits_v1(self, Engine engine)
get_region(self, str searxng_locale, default=None)
bool is_locale_supported(self, str searxng_locale)
get_language(self, str searxng_locale, default=None)
Union[ 'EngineTraits', None] fetch_traits(cls, Engine engine)
set_traits(self, Engine engine)