[mod] switching from pyright to basedpyright (plus first rules)

pyrightconfig.json :

  for the paths searx, searxng_extra and tests, individual rules were
  defined (for example, in test fewer / different rules are needed than in the
  searx package

searx/engines/__builtins__.pyi :

  The builtin types that are added to the global namespace of a module by the
  intended monkey patching of the engine modules / replaces the previous
  filtering of the stdout using grep.

test.pyright_modified (utils/lib_sxng_test.sh) :

  static type check of local modified files not yet commited

make test :

  prerequisite 'test.pyright' has been replaced by 'test.pyright_modified'

searx/engines/__init__.py, searx/enginelib/__init__.py :

  First, minimal typifications that were considered necessary.
This commit is contained in:
Markus Heiser 2025-07-31 13:45:16 +02:00 committed by Markus Heiser
parent 9bb04e288d
commit 25647c20d1
8 changed files with 125 additions and 37 deletions

View file

@ -47,7 +47,7 @@ search.checker.%: install
$(Q)./manage pyenv.cmd searxng-checker -v "$(subst _, ,$(patsubst search.checker.%,%,$@))"
PHONY += test ci.test test.shell
test: test.yamllint test.black test.pyright test.pylint test.unit test.robot test.rst test.shell test.shfmt
test: test.yamllint test.black test.pyright_modified test.pylint test.unit test.robot test.rst test.shell test.shfmt
ci.test: test test.pybabel
test.shell:
$(Q)shellcheck -x -s dash \
@ -80,7 +80,7 @@ MANAGE += node.env node.env.dev node.clean
MANAGE += py.build py.clean
MANAGE += pyenv pyenv.install pyenv.uninstall
MANAGE += format.python format.shell
MANAGE += test.yamllint test.pylint test.black test.pybabel test.unit test.coverage test.robot test.rst test.clean test.themes test.pyright test.shfmt
MANAGE += test.yamllint test.pylint test.black test.pybabel test.unit test.coverage test.robot test.rst test.clean test.themes test.pyright test.pyright_modified test.shfmt
MANAGE += themes.all themes.simple themes.simple.analyze themes.fix themes.lint themes.test
MANAGE += static.build.commit static.build.drop static.build.restore
MANAGE += nvm.install nvm.clean nvm.status nvm.nodejs

View file

@ -2,7 +2,6 @@
"name": "searxng.org/devtools",
"type": "module",
"dependencies": {
"pyright": "^1.1.403"
},
"scripts": {
"clean": "rm -Rf node_modules package-lock.json"

View file

@ -1,9 +1,48 @@
{
"venvPath": "local",
"venv": "py3",
"include": [
"searx",
"searxng_extra",
"tests"
]
"venvPath": "local",
"venv": "py3",
"include": [
"searx",
"searxng_extra",
"tests"
],
"reportAny" : "information",
"enableTypeIgnoreComments": true,
"reportIgnoreCommentWithoutRule": true,
"reportImplicitStringConcatenation": false,
"reportUninitializedInstanceVariable": false,
"reportUnnecessaryIsInstance": false,
"reportUnreachable": "information",
"reportUnusedCallResult": false,
"executionEnvironments": [
{
"root": "searx",
"extraPaths": ["."]
},
{
"root": "searxng_extra",
"extraPaths": ["."],
"reportAny" : false,
"reportUnknownMemberType": false,
"reportUnknownVariableType": false
},
{
"root": "tests",
"extraPaths": ["."],
"reportAny" : false,
"reportImplicitOverride": false,
"reportMissingParameterType": false,
"reportMissingTypeArgument": false,
"reportMissingTypeStubs": false,
"reportPrivateLocalImportUsage": false,
"reportPrivateUsage": false,
"reportUnannotatedClassAttribute": false,
"reportUnknownArgumentType": false,
"reportUnknownLambdaType": false,
"reportUnknownMemberType": false,
"reportUnknownParameterType": false,
"reportUnknownVariableType": false,
"reportUnusedParameter": false
}
]
}

View file

@ -22,3 +22,4 @@ coloredlogs==15.0.1
docutils>=0.21.2
parameterized==0.9.0
granian[reload]==2.5.0
basedpyright==1.31.0

View file

@ -252,7 +252,7 @@ class Engine: # pylint: disable=too-few-public-methods
display_error_messages: bool
"""Display error messages on the web UI."""
proxies: dict
proxies: dict[str, dict[str, str]]
"""Set proxies for a specific engine (YAML):
.. code:: yaml

View file

@ -0,0 +1,43 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""The builtin types that are added to the global namespace of a module by the
intended monkey patching of the engine modules.
.. attention::
Monkey-patching modules is a practice from the past that shouldn't be
expanded upon. In the long run, there should be an engine class that can be
inherited. However, as long as this class doesn't exist, and as long as all
engine modules aren't converted to an engine class, these builtin types will
still be needed.
"""
from __future__ import annotations
import logging
logger: logging.Logger
supported_languages: str
language_aliases: str
# from searx.engines.ENGINE_DEFAULT_ARGS
about: dict[str, dict[str, str | None | bool]]
categories: list[str]
disabled: bool
display_error_messages: bool
enable_http: bool
engine_type: str
inactive: bool
max_page: int
paging: int
safesearch: int
send_accept_language_header: bool
shortcut: str
time_range_support: int
timeout: int
tokens: list[str]
using_tor_proxy: bool
# from searx.engines.check_engine_module
network: str
# from searx.engines.update_attributes_for_tor
search_url: str

View file

@ -9,24 +9,26 @@ usage::
"""
from __future__ import annotations
import typing as t
import sys
import copy
from os.path import realpath, dirname
from typing import TYPE_CHECKING, Dict
import types
import inspect
from searx import logger, settings
from searx.utils import load_module
if TYPE_CHECKING:
if t.TYPE_CHECKING:
from searx.enginelib import Engine
logger = logger.getChild('engines')
ENGINE_DIR = dirname(realpath(__file__))
ENGINE_DEFAULT_ARGS = {
# Defaults for the namespace of an engine module, see load_engine()
ENGINE_DEFAULT_ARGS: dict[str, int | str | list[t.Any] | dict[str, t.Any] | bool] = {
# Common options in the engine module
"engine_type": "online",
"paging": False,
@ -49,11 +51,8 @@ ENGINE_DEFAULT_ARGS = {
# set automatically when an engine does not have any tab category
DEFAULT_CATEGORY = 'other'
# Defaults for the namespace of an engine module, see :py:func:`load_engine`
categories = {'general': []}
engines: Dict[str, Engine | types.ModuleType] = {}
categories: dict[str, list[str]] = {'general': []}
engines: dict[str, Engine | types.ModuleType] = {}
engine_shortcuts = {}
"""Simple map of registered *shortcuts* to name of the engine (or ``None``).
@ -77,7 +76,7 @@ def check_engine_module(module: types.ModuleType):
raise TypeError(msg)
def load_engine(engine_data: dict) -> Engine | types.ModuleType | None:
def load_engine(engine_data: dict[str, t.Any]) -> Engine | types.ModuleType | None:
"""Load engine from ``engine_data``.
:param dict engine_data: Attributes from YAML ``settings:engines/<engine>``

View file

@ -51,25 +51,32 @@ test.pylint() {
}
test.pyright() {
# use this pyright test for local tests in development / it suppress
# warnings related to intentional monkey patching but gives good hints where
# we need to work on SearXNG's typification.
# For integration into your IDE (editor) use the basedpyright-langserver
# (LSP) installed by 'pipx basedpyright' and read:
#
# - https://docs.basedpyright.com/latest/installation/ides/
#
# The $REPO_ROOT/pyrightconfig.json uses the virtualenv found in
# $REPO_ROOT/local/py3 and create by a 'make pyenv'
build_msg TEST "[pyright/types] static type check of python sources"
node.env.dev
build_msg TEST "[basedpyright] static type check of python sources"
LANG=C pyenv.cmd basedpyright
# ignore exit value from basedpyright
# dump_return $?
return 0
}
build_msg TEST "[pyright/types] suppress warnings related to intentional monkey patching"
# We run Pyright in the virtual environment because pyright executes
# "python" to determine the Python version.
pyenv.cmd npx --no-install pyright -p pyrightconfig.json |
grep -E '\.py:[0-9]+:[0-9]+' |
grep -v '/engines/.*.py.* - warning: "logger" is not defined' |
grep -v '/plugins/.*.py.* - error: "logger" is not defined' |
grep -v '/engines/.*.py.* - warning: "supported_languages" is not defined' |
grep -v '/engines/.*.py.* - warning: "language_aliases" is not defined' |
grep -v '/engines/.*.py.* - warning: "categories" is not defined'
# ignore exit value from pyright
# dump_return ${PIPESTATUS[0]}
test.pyright_modified() {
build_msg TEST "[basedpyright] static type check of local modified files"
local pyrigth_files=()
readarray -t pyrigth_files < <(git status --porcelain | awk 'match($2,".py[i]*$") {print $2}')
if [ ${#pyrigth_files[@]} -eq 0 ]; then
echo "there are no locally modified python files that could be checked"
else
pyenv.cmd basedpyright "${pyrigth_files[@]}"
fi
# ignore exit value from basedpyright
# dump_return $?
return 0
}