diff options
Diffstat (limited to 'searx/webapp.py')
| -rwxr-xr-x | searx/webapp.py | 69 |
1 files changed, 42 insertions, 27 deletions
diff --git a/searx/webapp.py b/searx/webapp.py index 3df9f2876..f509fea24 100755 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -14,8 +14,11 @@ from datetime import datetime, timedelta from timeit import default_timer from html import escape from io import StringIO +import typing +from typing import List, Dict, Iterable import urllib +import urllib.parse from urllib.parse import urlencode import httpx @@ -28,7 +31,6 @@ import flask from flask import ( Flask, - request, render_template, url_for, Response, @@ -55,6 +57,7 @@ from searx import ( searx_debug, ) from searx.data import ENGINE_DESCRIPTIONS +from searx.results import Timing, UnresponsiveEngine from searx.settings_defaults import OUTPUT_FORMATS from searx.settings_loader import get_default_settings_path from searx.exceptions import SearxParameterException @@ -89,7 +92,7 @@ from searx.utils import ( ) from searx.version import VERSION_STRING, GIT_URL, GIT_BRANCH from searx.query import RawTextQuery -from searx.plugins import plugins, initialize as plugin_initialize +from searx.plugins import Plugin, plugins, initialize as plugin_initialize from searx.plugins.oa_doi_rewrite import get_doi_resolver from searx.preferences import ( Preferences, @@ -224,6 +227,21 @@ exception_classname_to_text = { _flask_babel_get_translations = flask_babel.get_translations +class ExtendedRequest(flask.Request): + """This class is never initialized and only used for type checking.""" + + preferences: Preferences + errors: List[str] + user_plugins: List[Plugin] + form: Dict[str, str] + start_time: float + render_time: float + timings: List[Timing] + + +request = typing.cast(ExtendedRequest, flask.request) + + def _get_translations(): if has_request_context() and request.form.get('use-translation') == 'oc': babel_ext = flask_babel.current_app.extensions['babel'] @@ -321,7 +339,7 @@ def code_highlighter(codelines, language=None): return html_code -def get_current_theme_name(override=None): +def get_current_theme_name(override: str = None) -> str: """Returns theme name. Checks in this order: @@ -337,14 +355,14 @@ def get_current_theme_name(override=None): return theme_name -def get_result_template(theme_name, template_name): +def get_result_template(theme_name: str, template_name: str): themed_path = theme_name + '/result_templates/' + template_name if themed_path in result_templates: return themed_path return 'result_templates/' + template_name -def url_for_theme(endpoint, override_theme=None, **values): +def url_for_theme(endpoint: str, override_theme: str = None, **values): if endpoint == 'static' and values.get('filename'): theme_name = get_current_theme_name(override=override_theme) filename_with_theme = "themes/{}/{}".format(theme_name, values['filename']) @@ -354,7 +372,7 @@ def url_for_theme(endpoint, override_theme=None, **values): return url -def proxify(url): +def proxify(url: str): if url.startswith('//'): url = 'https:' + url @@ -369,7 +387,7 @@ def proxify(url): return '{0}?{1}'.format(settings['result_proxy']['url'], urlencode(url_params)) -def image_proxify(url): +def image_proxify(url: str): if url.startswith('//'): url = 'https:' + url @@ -405,7 +423,7 @@ def get_translations(): } -def _get_enable_categories(all_categories): +def _get_enable_categories(all_categories: Iterable[str]): disabled_engines = request.preferences.engines.get_disabled() enabled_categories = set( # pylint: disable=consider-using-dict-items @@ -417,14 +435,14 @@ def _get_enable_categories(all_categories): return [x for x in all_categories if x in enabled_categories] -def get_pretty_url(parsed_url): +def get_pretty_url(parsed_url: urllib.parse.ParseResult): path = parsed_url.path path = path[:-1] if len(path) > 0 and path[-1] == '/' else path path = path.replace("/", " › ") return [parsed_url.scheme + "://" + parsed_url.netloc, path] -def render(template_name, override_theme=None, **kwargs): +def render(template_name: str, override_theme: str = None, **kwargs): # values from the HTTP requests kwargs['endpoint'] = 'results' if 'q' in kwargs else request.endpoint kwargs['cookies'] = request.cookies @@ -552,7 +570,7 @@ def pre_request(): @app.after_request -def add_default_headers(response): +def add_default_headers(response: flask.Response): # set default http headers for header, value in settings['server']['default_http_headers'].items(): if header in response.headers: @@ -562,29 +580,28 @@ def add_default_headers(response): @app.after_request -def post_request(response): +def post_request(response: flask.Response): total_time = default_timer() - request.start_time timings_all = [ 'total;dur=' + str(round(total_time * 1000, 3)), 'render;dur=' + str(round(request.render_time * 1000, 3)), ] if len(request.timings) > 0: - timings = sorted(request.timings, key=lambda v: v['total']) + timings = sorted(request.timings, key=lambda t: t.total) timings_total = [ - 'total_' + str(i) + '_' + v['engine'] + ';dur=' + str(round(v['total'] * 1000, 3)) - for i, v in enumerate(timings) + 'total_' + str(i) + '_' + t.engine + ';dur=' + str(round(t.total * 1000, 3)) for i, t in enumerate(timings) ] timings_load = [ - 'load_' + str(i) + '_' + v['engine'] + ';dur=' + str(round(v['load'] * 1000, 3)) - for i, v in enumerate(timings) - if v.get('load') + 'load_' + str(i) + '_' + t.engine + ';dur=' + str(round(t.load * 1000, 3)) + for i, t in enumerate(timings) + if t.load ] timings_all = timings_all + timings_total + timings_load response.headers.add('Server-Timing', ', '.join(timings_all)) return response -def index_error(output_format, error_message): +def index_error(output_format: str, error_message: str): if output_format == 'json': return Response(json.dumps({'error': error_message}), mimetype='application/json') if output_format == 'csv': @@ -828,23 +845,21 @@ def search(): ) -def __get_translated_errors(unresponsive_engines): +def __get_translated_errors(unresponsive_engines: Iterable[UnresponsiveEngine]): translated_errors = [] # make a copy unresponsive_engines to avoid "RuntimeError: Set changed size # during iteration" it happens when an engine modifies the ResultContainer # after the search_multiple_requests method has stopped waiting - for unresponsive_engine in list(unresponsive_engines): - error_user_text = exception_classname_to_text.get(unresponsive_engine[1]) + for unresponsive_engine in unresponsive_engines: + error_user_text = exception_classname_to_text.get(unresponsive_engine.error_type) if not error_user_text: error_user_text = exception_classname_to_text[None] error_msg = gettext(error_user_text) - if unresponsive_engine[2]: - error_msg = "{} {}".format(error_msg, unresponsive_engine[2]) - if unresponsive_engine[3]: + if unresponsive_engine.suspended: error_msg = gettext('Suspended') + ': ' + error_msg - translated_errors.append((unresponsive_engine[0], error_msg)) + translated_errors.append((unresponsive_engine.engine, error_msg)) return sorted(translated_errors, key=lambda e: e[0]) @@ -1060,7 +1075,7 @@ def preferences(): ) -def _is_selected_language_supported(engine, preferences): # pylint: disable=redefined-outer-name +def _is_selected_language_supported(engine, preferences: Preferences): # pylint: disable=redefined-outer-name language = preferences.get_value('language') if language == 'all': return True |