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 
   19from searx 
import locales
 
   27    """Encodes :class:`EngineTraits` to a serializable object, see 
   28    :class:`json.JSONEncoder`.""" 
   31        """Return dictionary of a :class:`EngineTraits` object.""" 
   32        if isinstance(o, EngineTraits):
 
 
 
   39    """The class is intended to be instantiated for each engine.""" 
   41    regions: dict[str, str] = dataclasses.field(default_factory=dict)
 
   42    """Maps SearXNG's internal representation of a region to the one of the engine. 
   44    SearXNG's internal representation can be parsed by babel and the value is 
   50           'fr-BE' : <engine's region name>, 
   53       for key, egnine_region regions.items(): 
   54          searxng_region = babel.Locale.parse(key, sep='-') 
   58    languages: dict[str, str] = dataclasses.field(default_factory=dict)
 
   59    """Maps SearXNG's internal representation of a language to the one of the engine. 
   61    SearXNG's internal representation can be parsed by babel and the value is 
   67           'ca' : <engine's language name>, 
   70       for key, egnine_lang in languages.items(): 
   71          searxng_lang = babel.Locale.parse(key) 
   75    all_locale: str | 
None = 
None 
   76    """To which locale value SearXNG's ``all`` language is mapped (shown a "Default 
   80    data_type: t.Literal[
'traits_v1'] = 
'traits_v1' 
   81    """Data type, default is 'traits_v1'. 
   84    custom: dict[str, t.Any] = dataclasses.field(default_factory=dict)
 
   85    """A place to store engine's custom traits, not related to the SearXNG core. 
   88    def get_language(self, searxng_locale: str, default: t.Any = 
None):
 
   89        """Return engine's language string that *best fits* to SearXNG's locale. 
   91        :param searxng_locale: SearXNG's internal representation of locale 
   94        :param default: engine's default language 
   96        The *best fits* rules are implemented in 
   97        :py:obj:`searx.locales.get_engine_locale`.  Except for the special value ``all`` 
   98        which is determined from :py:obj:`EngineTraits.all_locale`. 
  100        if searxng_locale == 
'all' and self.
all_locale is not None:
 
  102        return locales.get_engine_locale(searxng_locale, self.
languages, default=default)
 
 
  104    def get_region(self, searxng_locale: str, default: t.Any = 
None) -> t.Any:
 
  105        """Return engine's region string that best fits to SearXNG's locale. 
  107        :param searxng_locale: SearXNG's internal representation of locale 
  108          selected by the user. 
  110        :param default: engine's default region 
  112        The *best fits* rules are implemented in 
  113        :py:obj:`searx.locales.get_engine_locale`.  Except for the special value ``all`` 
  114        which is determined from :py:obj:`EngineTraits.all_locale`. 
  116        if searxng_locale == 
'all' and self.
all_locale is not None:
 
  118        return locales.get_engine_locale(searxng_locale, self.
regions, default=default)
 
 
  121        """A *locale* (SearXNG's internal representation) is considered to be 
  122        supported by the engine if the *region* or the *language* is supported 
  125        For verification the functions :py:func:`EngineTraits.get_region` and 
  126        :py:func:`EngineTraits.get_language` are used. 
  131        raise TypeError(
'engine traits of type %s is unknown' % self.
data_type)
 
 
  134        """Create a copy of the dataclass object.""" 
 
  138    def fetch_traits(cls, engine: 
"Engine | types.ModuleType") -> 
"EngineTraits | None":
 
  139        """Call a function ``fetch_traits(engine_traits)`` from engines namespace to fetch 
  140        and set properties from the origin engine in the object ``engine_traits``.  If 
  141        function does not exists, ``None`` is returned. 
  144        fetch_traits = getattr(engine, 
'fetch_traits', 
None)
 
  148            engine_traits = cls()
 
 
  153        """Set traits from self object in a :py:obj:`.Engine` namespace. 
  155        :param engine: engine instance build by :py:func:`searx.engines.load_engine` 
  161            raise TypeError(
'engine traits of type %s is unknown' % self.
data_type)
 
 
  174        _msg = 
"settings.yml - engine: '%s' / %s: '%s' not supported" 
  176        languages = traits.languages
 
  177        if hasattr(engine, 
'language'):
 
  178            if engine.language 
not in languages:
 
  179                raise ValueError(_msg % (engine.name, 
'language', engine.language))
 
  180            traits.languages = {engine.language: languages[engine.language]}
 
  182        regions = traits.regions
 
  183        if hasattr(engine, 
'region'):
 
  184            if engine.region 
not in regions:
 
  185                raise ValueError(_msg % (engine.name, 
'region', engine.region))
 
  186            traits.regions = {engine.region: regions[engine.region]}
 
  188        engine.language_support = bool(traits.languages 
or traits.regions)  
 
  191        engine.traits = traits  
 
 
 
  195    """A python dictionary to map :class:`EngineTraits` by engine name.""" 
  197    ENGINE_TRAITS_FILE: pathlib.Path = (data_dir / 
'engine_traits.json').resolve()
 
  198    """File with persistence of the :py:obj:`EngineTraitsMap`.""" 
  201        """Store EngineTraitsMap in in file :py:obj:`self.ENGINE_TRAITS_FILE`""" 
  203            json.dump(self, f, indent=2, sort_keys=
True, cls=EngineTraitsEncoder)
 
 
  207        """Instantiate :class:`EngineTraitsMap` object from :py:obj:`ENGINE_TRAITS`""" 
  209        for k, v 
in ENGINE_TRAITS.items():
 
 
  214    def fetch_traits(cls, log: t.Callable[[str], 
None]) -> 
'EngineTraitsMap':
 
  215        from searx 
import engines  
 
  217        names = list(engines.engines)
 
  221        for engine_name 
in names:
 
  222            engine: Engine | types.ModuleType = engines.engines[engine_name]
 
  227                traits = EngineTraits.fetch_traits(engine)
 
  228            except Exception 
as exc:
 
  229                log(
"FATAL: while fetch_traits %s: %s" % (engine_name, exc))
 
  230                if os.environ.get(
'FORCE', 
'').lower() 
not in [
'on', 
'true', 
'1']:
 
  232                v = ENGINE_TRAITS.get(engine_name)
 
  234                    log(
"FORCE: re-use old values from fetch_traits - ENGINE_TRAITS[%s]" % engine_name)
 
  237            if traits 
is not None:
 
  238                log(
"%-20s: SearXNG languages --> %s " % (engine_name, len(traits.languages)))
 
  239                log(
"%-20s: SearXNG regions   --> %s" % (engine_name, len(traits.regions)))
 
  240                obj[engine_name] = traits
 
 
  245        """Set traits in a :py:obj:`Engine` namespace. 
  247        :param engine: engine instance build by :py:func:`searx.engines.load_engine` 
  251        if engine.name 
in self.keys():
 
  252            engine_traits = self[engine.name]
 
  254        elif engine.engine 
in self.keys():
 
  261            engine_traits = self[engine.engine]
 
  263        engine_traits.set_traits(engine)
 
 
 
t.Any default(self, t.Any o)
 'EngineTraitsMap' fetch_traits(cls, t.Callable[[str], None] log)
set_traits(self, "Engine | types.ModuleType" engine)
 'EngineTraitsMap' from_data(cls)
"EngineTraits | None" fetch_traits(cls, "Engine | types.ModuleType" engine)
t.Any get_region(self, str searxng_locale, t.Any default=None)
bool is_locale_supported(self, str searxng_locale)
_set_traits_v1(self, "Engine | types.ModuleType" engine)
get_language(self, str searxng_locale, t.Any default=None)
set_traits(self, "Engine | types.ModuleType" engine)