From df3344e5d5fdfd2425324d5e10e8c8e5104963b0 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Tue, 28 Jan 2025 16:51:07 +0100 Subject: [PATCH] [fix] JSON & CSV format return error 500 For CSV and JSON output, the LegacyResult and the Result objects needs to be converted to a python dictionary. Closes: https://github.com/searxng/searxng/issues/4244 Signed-off-by: Markus Heiser --- searx/result_types/_base.py | 7 ++++++- searx/webutils.py | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/searx/result_types/_base.py b/searx/result_types/_base.py index 6d9a49943..66e31c7f2 100644 --- a/searx/result_types/_base.py +++ b/searx/result_types/_base.py @@ -110,6 +110,9 @@ class Result(msgspec.Struct, kw_only=True): return iter(self.__struct_fields__) + def as_dict(self): + return {f: getattr(self, f) for f in self.__struct_fields__} + class LegacyResult(dict): """A wrapper around a legacy result item. The SearXNG core uses this class @@ -130,10 +133,12 @@ class LegacyResult(dict): UNSET = object() WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U) + def as_dict(self): + return self + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.__dict__ = self # Init fields with defaults / compare with defaults of the fields in class Result self.engine = self.get("engine", "") diff --git a/searx/webutils.py b/searx/webutils.py index c58b981f6..6e49e3830 100644 --- a/searx/webutils.py +++ b/searx/webutils.py @@ -123,17 +123,18 @@ def write_csv_response(csv: CSVWriter, rc: ResultContainer) -> None: # pylint: """ - results = rc.get_ordered_results() keys = ('title', 'url', 'content', 'host', 'engine', 'score', 'type') csv.writerow(keys) - for row in results: + for res in rc.get_ordered_results(): + row = res.as_dict() row['host'] = row['parsed_url'].netloc row['type'] = 'result' csv.writerow([row.get(key, '') for key in keys]) for a in rc.answers: - row = {'title': a, 'type': 'answer'} + row = a.as_dict() + row['host'] = row['parsed_url'].netloc csv.writerow([row.get(key, '') for key in keys]) for a in rc.suggestions: @@ -158,18 +159,17 @@ class JSONEncoder(json.JSONEncoder): # pylint: disable=missing-class-docstring def get_json_response(sq: SearchQuery, rc: ResultContainer) -> str: """Returns the JSON string of the results to a query (``application/json``)""" - results = rc.number_of_results - x = { + data = { 'query': sq.query, - 'number_of_results': results, - 'results': rc.get_ordered_results(), - 'answers': list(rc.answers), + 'number_of_results': rc.number_of_results, + 'results': [_.as_dict() for _ in rc.get_ordered_results()], + 'answers': [_.as_dict() for _ in rc.answers], 'corrections': list(rc.corrections), 'infoboxes': rc.infoboxes, 'suggestions': list(rc.suggestions), 'unresponsive_engines': get_translated_errors(rc.unresponsive_engines), } - response = json.dumps(x, cls=JSONEncoder) + response = json.dumps(data, cls=JSONEncoder) return response