mirror of
https://github.com/searxng/searxng.git
synced 2025-09-06 10:18:31 +02:00
[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
This commit is contained in:
parent
09500459fe
commit
57b9673efb
107 changed files with 1205 additions and 1251 deletions
|
@ -2,8 +2,9 @@
|
|||
"""Implementation of the default settings.
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
import typing as t
|
||||
import numbers
|
||||
import errno
|
||||
import os
|
||||
|
@ -11,6 +12,7 @@ import logging
|
|||
from base64 import b64decode
|
||||
from os.path import dirname, abspath
|
||||
|
||||
from typing_extensions import override
|
||||
from .sxng_locales import sxng_locales
|
||||
|
||||
searx_dir = abspath(dirname(__file__))
|
||||
|
@ -19,7 +21,7 @@ logger = logging.getLogger('searx')
|
|||
OUTPUT_FORMATS = ['html', 'csv', 'json', 'rss']
|
||||
SXNG_LOCALE_TAGS = ['all', 'auto'] + list(l[0] for l in sxng_locales)
|
||||
SIMPLE_STYLE = ('auto', 'light', 'dark', 'black')
|
||||
CATEGORIES_AS_TABS = {
|
||||
CATEGORIES_AS_TABS: dict[str, dict[str, t.Any]] = {
|
||||
'general': {},
|
||||
'images': {},
|
||||
'videos': {},
|
||||
|
@ -41,35 +43,50 @@ STR_TO_BOOL = {
|
|||
}
|
||||
_UNDEFINED = object()
|
||||
|
||||
# This type definition for SettingsValue.type_definition is incomplete, but it
|
||||
# helps to significantly reduce the most common error messages regarding type
|
||||
# annotations.
|
||||
TypeDefinition: t.TypeAlias = ( # pylint: disable=invalid-name
|
||||
tuple[None, bool, type]
|
||||
| tuple[None, type, type]
|
||||
| tuple[None, type]
|
||||
| tuple[bool, type]
|
||||
| tuple[type, type]
|
||||
| tuple[type]
|
||||
| tuple[str | int, ...]
|
||||
)
|
||||
|
||||
TypeDefinitionArg: t.TypeAlias = type | TypeDefinition # pylint: disable=invalid-name
|
||||
|
||||
|
||||
class SettingsValue:
|
||||
"""Check and update a setting value"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type_definition: typing.Union[None, typing.Any, typing.Tuple[typing.Any]] = None,
|
||||
default: typing.Any = None,
|
||||
environ_name: str = None,
|
||||
type_definition_arg: TypeDefinitionArg,
|
||||
default: t.Any = None,
|
||||
environ_name: str | None = None,
|
||||
):
|
||||
self.type_definition = (
|
||||
type_definition if type_definition is None or isinstance(type_definition, tuple) else (type_definition,)
|
||||
self.type_definition: TypeDefinition = (
|
||||
type_definition_arg if isinstance(type_definition_arg, tuple) else (type_definition_arg,)
|
||||
)
|
||||
self.default = default
|
||||
self.environ_name = environ_name
|
||||
self.default: t.Any = default
|
||||
self.environ_name: str | None = environ_name
|
||||
|
||||
@property
|
||||
def type_definition_repr(self):
|
||||
types_str = [t.__name__ if isinstance(t, type) else repr(t) for t in self.type_definition]
|
||||
types_str = [td.__name__ if isinstance(td, type) else repr(td) for td in self.type_definition]
|
||||
return ', '.join(types_str)
|
||||
|
||||
def check_type_definition(self, value: typing.Any) -> None:
|
||||
def check_type_definition(self, value: t.Any) -> None:
|
||||
if value in self.type_definition:
|
||||
return
|
||||
type_list = tuple(t for t in self.type_definition if isinstance(t, type))
|
||||
if not isinstance(value, type_list):
|
||||
raise ValueError('The value has to be one of these types/values: {}'.format(self.type_definition_repr))
|
||||
|
||||
def __call__(self, value: typing.Any) -> typing.Any:
|
||||
def __call__(self, value: t.Any) -> t.Any:
|
||||
if value == _UNDEFINED:
|
||||
value = self.default
|
||||
# override existing value with environ
|
||||
|
@ -85,7 +102,8 @@ class SettingsValue:
|
|||
class SettingSublistValue(SettingsValue):
|
||||
"""Check the value is a sublist of type definition."""
|
||||
|
||||
def check_type_definition(self, value: typing.Any) -> typing.Any:
|
||||
@override
|
||||
def check_type_definition(self, value: list[t.Any]) -> None:
|
||||
if not isinstance(value, list):
|
||||
raise ValueError('The value has to a list')
|
||||
for item in value:
|
||||
|
@ -96,12 +114,14 @@ class SettingSublistValue(SettingsValue):
|
|||
class SettingsDirectoryValue(SettingsValue):
|
||||
"""Check and update a setting value that is a directory path"""
|
||||
|
||||
def check_type_definition(self, value: typing.Any) -> typing.Any:
|
||||
@override
|
||||
def check_type_definition(self, value: t.Any) -> t.Any:
|
||||
super().check_type_definition(value)
|
||||
if not os.path.isdir(value):
|
||||
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), value)
|
||||
|
||||
def __call__(self, value: typing.Any) -> typing.Any:
|
||||
@override
|
||||
def __call__(self, value: t.Any) -> t.Any:
|
||||
if value == '':
|
||||
value = self.default
|
||||
return super().__call__(value)
|
||||
|
@ -110,13 +130,14 @@ class SettingsDirectoryValue(SettingsValue):
|
|||
class SettingsBytesValue(SettingsValue):
|
||||
"""str are base64 decoded"""
|
||||
|
||||
def __call__(self, value: typing.Any) -> typing.Any:
|
||||
@override
|
||||
def __call__(self, value: t.Any) -> t.Any:
|
||||
if isinstance(value, str):
|
||||
value = b64decode(value)
|
||||
return super().__call__(value)
|
||||
|
||||
|
||||
def apply_schema(settings, schema, path_list):
|
||||
def apply_schema(settings: dict[str, t.Any], schema: dict[str, t.Any], path_list: list[str]):
|
||||
error = False
|
||||
for key, value in schema.items():
|
||||
if isinstance(value, SettingsValue):
|
||||
|
@ -135,7 +156,7 @@ def apply_schema(settings, schema, path_list):
|
|||
return error
|
||||
|
||||
|
||||
SCHEMA = {
|
||||
SCHEMA: dict[str, t.Any] = {
|
||||
'general': {
|
||||
'debug': SettingsValue(bool, False, 'SEARXNG_DEBUG'),
|
||||
'instance_name': SettingsValue(str, 'SearXNG'),
|
||||
|
@ -159,7 +180,7 @@ SCHEMA = {
|
|||
'autocomplete_min': SettingsValue(int, 4),
|
||||
'favicon_resolver': SettingsValue(str, ''),
|
||||
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
|
||||
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
|
||||
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS), # type: ignore
|
||||
'ban_time_on_fail': SettingsValue(numbers.Real, 5),
|
||||
'max_ban_time_on_fail': SettingsValue(numbers.Real, 120),
|
||||
'suspended_times': {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue