summaryrefslogtreecommitdiff
path: root/searx/preferences.py
diff options
context:
space:
mode:
authorMarkus Heiser <markus.heiser@darmarit.de>2025-08-22 17:17:51 +0200
committerMarkus Heiser <markus.heiser@darmarIT.de>2025-09-03 13:37:36 +0200
commit57b9673efb1b4fd18a3ac15e26da642201e2cd33 (patch)
tree79d3ecd365a1669a1109aa7e5dd3636bc1041d96 /searx/preferences.py
parent09500459feffa414dc7a0601bdb164464a8b0454 (diff)
[mod] addition of various type hints / tbc
- pyright configuration [1]_ - stub files: types-lxml [2]_ - addition of various type hints - enable use of new type system features on older Python versions [3]_ - ``.tool-versions`` - set python to lowest version we support (3.10.18) [4]_: Older versions typically lack some typing features found in newer Python versions. Therefore, for local type checking (before commit), it is necessary to use the older Python interpreter. .. [1] https://docs.basedpyright.com/v1.20.0/configuration/config-files/ .. [2] https://pypi.org/project/types-lxml/ .. [3] https://typing-extensions.readthedocs.io/en/latest/# .. [4] https://mise.jdx.dev/configuration.html#tool-versions Signed-off-by: Markus Heiser <markus.heiser@darmarit.de> Format: reST
Diffstat (limited to 'searx/preferences.py')
-rw-r--r--searx/preferences.py81
1 files changed, 48 insertions, 33 deletions
diff --git a/searx/preferences.py b/searx/preferences.py
index eadb0970c..42dbe4702 100644
--- a/searx/preferences.py
+++ b/searx/preferences.py
@@ -5,14 +5,17 @@ from __future__ import annotations
# pylint: disable=useless-object-inheritance
+import typing as t
+
from base64 import urlsafe_b64encode, urlsafe_b64decode
from zlib import compress, decompress
from urllib.parse import parse_qs, urlencode
-from typing import Iterable, Dict, List, Optional
from collections import OrderedDict
+from collections.abc import Iterable
import flask
import babel
+import babel.core
import searx.plugins
@@ -27,7 +30,7 @@ from searx.webutils import VALID_LANGUAGE_CODE
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
DOI_RESOLVERS = list(settings['doi_resolvers'])
-MAP_STR2BOOL: Dict[str, bool] = OrderedDict(
+MAP_STR2BOOL: dict[str, bool] = OrderedDict(
[
('0', False),
('1', True),
@@ -47,10 +50,10 @@ class ValidationException(Exception):
class Setting:
"""Base class of user settings"""
- def __init__(self, default_value, locked: bool = False):
+ def __init__(self, default_value: t.Any, locked: bool = False):
super().__init__()
- self.value = default_value
- self.locked = locked
+ self.value: t.Any = default_value
+ self.locked: bool = locked
def parse(self, data: str):
"""Parse ``data`` and store the result at ``self.value``
@@ -80,9 +83,11 @@ class StringSetting(Setting):
class EnumStringSetting(Setting):
"""Setting of a value which can only come from the given choices"""
- def __init__(self, default_value: str, choices: Iterable[str], locked=False):
+ value: str
+
+ def __init__(self, default_value: str, choices: Iterable[str], locked: bool = False):
super().__init__(default_value, locked)
- self.choices = choices
+ self.choices: Iterable[str] = choices
self._validate_selection(self.value)
def _validate_selection(self, selection: str):
@@ -98,12 +103,12 @@ class EnumStringSetting(Setting):
class MultipleChoiceSetting(Setting):
"""Setting of values which can only come from the given choices"""
- def __init__(self, default_value: List[str], choices: Iterable[str], locked=False):
+ def __init__(self, default_value: list[str], choices: Iterable[str], locked: bool = False):
super().__init__(default_value, locked)
- self.choices = choices
+ self.choices: Iterable[str] = choices
self._validate_selections(self.value)
- def _validate_selections(self, selections: List[str]):
+ def _validate_selections(self, selections: list[str]):
for item in selections:
if item not in self.choices:
raise ValidationException('Invalid value: "{0}"'.format(selections))
@@ -111,14 +116,14 @@ class MultipleChoiceSetting(Setting):
def parse(self, data: str):
"""Parse and validate ``data`` and store the result at ``self.value``"""
if data == '':
- self.value = []
+ self.value: list[str] = []
return
elements = data.split(',')
self._validate_selections(elements)
self.value = elements
- def parse_form(self, data: List[str]):
+ def parse_form(self, data: list[str]):
if self.locked:
return
@@ -135,9 +140,9 @@ class MultipleChoiceSetting(Setting):
class SetSetting(Setting):
"""Setting of values of type ``set`` (comma separated string)"""
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.values = set()
+ def __init__(self, *args, **kwargs): # type: ignore
+ super().__init__(*args, **kwargs) # type: ignore
+ self.values: set[str] = set()
def get_value(self):
"""Returns a string with comma separated values."""
@@ -168,7 +173,9 @@ class SetSetting(Setting):
class SearchLanguageSetting(EnumStringSetting):
"""Available choices may change, so user's value may not be in choices anymore"""
- def _validate_selection(self, selection):
+ value: str
+
+ def _validate_selection(self, selection: str):
if selection != '' and selection != 'auto' and not VALID_LANGUAGE_CODE.match(selection):
raise ValidationException('Invalid language code: "{0}"'.format(selection))
@@ -192,9 +199,14 @@ class SearchLanguageSetting(EnumStringSetting):
class MapSetting(Setting):
"""Setting of a value that has to be translated in order to be storable"""
- def __init__(self, default_value, map: Dict[str, object], locked=False): # pylint: disable=redefined-builtin
+ key: str
+ value: object
+
+ def __init__(
+ self, default_value: object, map: dict[str, object], locked: bool = False
+ ): # pylint: disable=redefined-builtin
super().__init__(default_value, locked)
- self.map = map
+ self.map: dict[str, object] = map
if self.value not in self.map.values():
raise ValidationException('Invalid default value')
@@ -216,7 +228,10 @@ class MapSetting(Setting):
class BooleanSetting(Setting):
"""Setting of a boolean value that has to be translated in order to be storable"""
- def normalized_str(self, val):
+ value: bool
+ key: str
+
+ def normalized_str(self, val: t.Any) -> str:
for v_str, v_obj in MAP_STR2BOOL.items():
if val == v_obj:
return v_str
@@ -236,11 +251,11 @@ class BooleanSetting(Setting):
class BooleanChoices:
"""Maps strings to booleans that are either true or false."""
- def __init__(self, name: str, choices: Dict[str, bool], locked: bool = False):
- self.name = name
- self.choices = choices
- self.locked = locked
- self.default_choices = dict(choices)
+ def __init__(self, name: str, choices: dict[str, bool], locked: bool = False):
+ self.name: str = name
+ self.choices: dict[str, bool] = choices
+ self.locked: bool = locked
+ self.default_choices: dict[str, bool] = dict(choices)
def transform_form_items(self, items):
return items
@@ -257,7 +272,7 @@ class BooleanChoices:
if enabled in self.choices:
self.choices[enabled] = True
- def parse_form(self, items: List[str]):
+ def parse_form(self, items: list[str]):
if self.locked:
return
@@ -327,10 +342,10 @@ class ClientPref:
# hint: searx.webapp.get_client_settings should be moved into this class
- locale: babel.Locale
+ locale: babel.Locale | None
"""Locale preferred by the client."""
- def __init__(self, locale: Optional[babel.Locale] = None):
+ def __init__(self, locale: babel.Locale | None = None):
self.locale = locale
@property
@@ -354,7 +369,7 @@ class ClientPref:
if not al_header:
return cls(locale=None)
- pairs = []
+ pairs: list[tuple[babel.Locale, float]] = []
for l in al_header.split(','):
# fmt: off
lang, qvalue = [_.strip() for _ in (l.split(';') + ['q=1',])[:2]]
@@ -387,7 +402,7 @@ class Preferences:
super().__init__()
- self.key_value_settings: Dict[str, Setting] = {
+ self.key_value_settings: dict[str, Setting] = {
# fmt: off
'categories': MultipleChoiceSetting(
['general'],
@@ -516,7 +531,7 @@ class Preferences:
dict_data[x] = y[0]
self.parse_dict(dict_data)
- def parse_dict(self, input_data: Dict[str, str]):
+ def parse_dict(self, input_data: dict[str, str]):
"""parse preferences from request (``flask.request.form``)"""
for user_setting_name, user_setting in input_data.items():
if user_setting_name in self.key_value_settings:
@@ -530,7 +545,7 @@ class Preferences:
elif user_setting_name == 'tokens':
self.tokens.parse(user_setting)
- def parse_form(self, input_data: Dict[str, str]):
+ def parse_form(self, input_data: dict[str, str]):
"""Parse formular (``<input>``) data from a ``flask.request.form``"""
disabled_engines = []
enabled_categories = []
@@ -554,12 +569,12 @@ class Preferences:
elif user_setting_name == 'tokens':
self.tokens.parse_form(user_setting)
- self.key_value_settings['categories'].parse_form(enabled_categories)
+ self.key_value_settings['categories'].parse_form(enabled_categories) # type: ignore
self.engines.parse_form(disabled_engines)
self.plugins.parse_form(disabled_plugins)
# cannot be used in case of engines or plugins
- def get_value(self, user_setting_name: str):
+ def get_value(self, user_setting_name: str) -> t.Any:
"""Returns the value for ``user_setting_name``"""
ret_val = None
if user_setting_name in self.key_value_settings: