diff --git a/docs/build-templates/searxng.rst b/docs/build-templates/searxng.rst index 14b385468..bc5d3e8fc 100644 --- a/docs/build-templates/searxng.rst +++ b/docs/build-templates/searxng.rst @@ -123,7 +123,7 @@ ${fedora_build} # jump to SearXNG's working tree and install SearXNG into virtualenv (${SERVICE_USER})$ cd \"$SEARXNG_SRC\" - (${SERVICE_USER})$ pip install -e . + (${SERVICE_USER})$ pip install --use-pep517 --no-build-isolation -e . .. END manage.sh update_packages diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst index 3c3b2bf3b..383113bae 100644 --- a/docs/dev/makefile.rst +++ b/docs/dev/makefile.rst @@ -61,7 +61,7 @@ working tree and release a ``make install`` to get a virtualenv with a $ make install PYENV [virtualenv] installing ./requirements*.txt into local/py3 ... - PYENV [install] pip install -e 'searx[test]' + PYENV [install] pip install --use-pep517 --no-build-isolation -e 'searx[test]' ... Successfully installed searxng-2023.7.19+a446dea1b @@ -78,7 +78,7 @@ the check fails if you edit the requirements listed in ... PYENV [virtualenv] installing ./requirements*.txt into local/py3 ... - PYENV [install] pip install -e 'searx[test]' + PYENV [install] pip install --use-pep517 --no-build-isolation -e 'searx[test]' ... Successfully installed searxng-2023.7.19+a446dea1b diff --git a/manage b/manage index 155a1f2dd..7edcb1f5a 100755 --- a/manage +++ b/manage @@ -233,7 +233,7 @@ gecko.driver() { build_msg INSTALL "geckodriver already installed" return fi - PLATFORM="$(python3 -c 'import platform; print(platform.system().lower(), platform.architecture()[0])')" + PLATFORM="$(python -c 'import platform; print(platform.system().lower(), platform.architecture()[0])')" case "$PLATFORM" in "linux 32bit" | "linux2 32bit") ARCH="linux32";; "linux 64bit" | "linux2 64bit") ARCH="linux64";; @@ -299,8 +299,8 @@ pyenv.install() { ( set -e pyenv - build_msg PYENV "[install] pip install -e 'searx${PY_SETUP_EXTRAS}'" - "${PY_ENV_BIN}/python" -m pip install -e ".${PY_SETUP_EXTRAS}" + build_msg PYENV "[install] pip install --use-pep517 --no-build-isolation -e 'searx${PY_SETUP_EXTRAS}'" + "${PY_ENV_BIN}/python" -m pip install --use-pep517 --no-build-isolation -e ".${PY_SETUP_EXTRAS}" ) local exit_val=$? if [ ! $exit_val -eq 0 ]; then diff --git a/searx/engines/tineye.py b/searx/engines/tineye.py index 196c89a2b..c35799c69 100644 --- a/searx/engines/tineye.py +++ b/searx/engines/tineye.py @@ -14,10 +14,16 @@ billion images `[tineye.com] `_. """ +from typing import TYPE_CHECKING from urllib.parse import urlencode from datetime import datetime from flask_babel import gettext +if TYPE_CHECKING: + import logging + + logger = logging.getLogger() + about = { "website": 'https://tineye.com', "wikidata_id": 'Q2382535', @@ -34,7 +40,7 @@ categories = ['general'] paging = True safesearch = False base_url = 'https://tineye.com' -search_string = '/result_json/?page={page}&{query}' +search_string = '/api/v1/result_json/?page={page}&{query}' FORMAT_NOT_SUPPORTED = gettext( "Could not read that image url. This may be due to an unsupported file" @@ -120,7 +126,7 @@ def parse_tineye_match(match_json): crawl_date = backlink_json.get("crawl_date") if crawl_date: - crawl_date = datetime.fromisoformat(crawl_date[:-3]) + crawl_date = datetime.strptime(crawl_date, '%Y-%m-%d') else: crawl_date = datetime.min @@ -150,29 +156,15 @@ def parse_tineye_match(match_json): def response(resp): """Parse HTTP response from TinEye.""" - results = [] - try: + # handle the 422 client side errors, and the possible 400 status code error + if resp.status_code in (400, 422): json_data = resp.json() - except Exception as exc: # pylint: disable=broad-except - msg = "can't parse JSON response // %s" % exc - logger.error(msg) - json_data = {'error': msg} - - # handle error codes from Tineye - - if resp.is_error: - if resp.status_code in (400, 422): - - message = 'HTTP status: %s' % resp.status_code - error = json_data.get('error') - s_key = json_data.get('suggestions', {}).get('key', '') - - if error and s_key: - message = "%s (%s)" % (error, s_key) - elif error: - message = error + suggestions = json_data.get('suggestions', {}) + message = f'HTTP Status Code: {resp.status_code}' + if resp.status_code == 422: + s_key = suggestions.get('key', '') if s_key == "Invalid image URL": # test https://docs.searxng.org/_static/searxng-wordmark.svg message = FORMAT_NOT_SUPPORTED @@ -182,16 +174,23 @@ def response(resp): elif s_key == 'Download Error': # test https://notexists message = DOWNLOAD_ERROR + else: + logger.warning("Unknown suggestion key encountered: %s", s_key) + else: # 400 + description = suggestions.get('description') + if isinstance(description, list): + message = ','.join(description) - # see https://github.com/searxng/searxng/pull/1456#issuecomment-1193105023 - # results.append({'answer': message}) - logger.error(message) + # see https://github.com/searxng/searxng/pull/1456#issuecomment-1193105023 + # results.append({'answer': message}) + logger.error(message) + return [] - return results + # Raise for all other responses + resp.raise_for_status() - resp.raise_for_status() - - # append results from matches + results = [] + json_data = resp.json() for match_json in json_data['matches']: diff --git a/searx/engines/yandex.py b/searx/engines/yandex.py new file mode 100644 index 000000000..2c6984fdc --- /dev/null +++ b/searx/engines/yandex.py @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Yandex (Web, images)""" + +from json import loads +from urllib.parse import urlencode +from html import unescape +from lxml import html +from searx.exceptions import SearxEngineCaptchaException +from searx.utils import humanize_bytes, eval_xpath, eval_xpath_list, extract_text, extr + + +# Engine metadata +about = { + "website": 'https://yandex.com/', + "wikidata_id": 'Q5281', + "official_api_documentation": "?", + "use_official_api": False, + "require_api_key": False, + "results": 'HTML', +} + +# Engine configuration +categories = [] +paging = True +search_type = "" + +# Search URL +base_url_web = 'https://yandex.com/search/site/' +base_url_images = 'https://yandex.com/images/search' + +results_xpath = '//li[contains(@class, "serp-item")]' +url_xpath = './/a[@class="b-serp-item__title-link"]/@href' +title_xpath = './/h3[@class="b-serp-item__title"]/a[@class="b-serp-item__title-link"]/span' +content_xpath = './/div[@class="b-serp-item__content"]//div[@class="b-serp-item__text"]' + + +def catch_bad_response(resp): + if resp.url.path.startswith('/showcaptcha'): + raise SearxEngineCaptchaException() + + +def request(query, params): + query_params_web = { + "tmpl_version": "releases", + "text": query, + "web": "1", + "frame": "1", + "searchid": "3131712", + } + + query_params_images = { + "text": query, + "uinfo": "sw-1920-sh-1080-ww-1125-wh-999", + } + + if params['pageno'] > 1: + query_params_web.update({"p": params["pageno"] - 1}) + query_params_images.update({"p": params["pageno"] - 1}) + + params["cookies"] = {'cookie': "yp=1716337604.sp.family%3A0#1685406411.szm.1:1920x1080:1920x999"} + + if search_type == 'web': + params['url'] = f"{base_url_web}?{urlencode(query_params_web)}" + elif search_type == 'images': + params['url'] = f"{base_url_images}?{urlencode(query_params_images)}" + + return params + + +def response(resp): + if search_type == 'web': + + catch_bad_response(resp) + + dom = html.fromstring(resp.text) + + results = [] + + for result in eval_xpath_list(dom, results_xpath): + results.append( + { + 'url': extract_text(eval_xpath(result, url_xpath)), + 'title': extract_text(eval_xpath(result, title_xpath)), + 'content': extract_text(eval_xpath(result, content_xpath)), + } + ) + + return results + + if search_type == 'images': + + catch_bad_response(resp) + + html_data = html.fromstring(resp.text) + html_sample = unescape(html.tostring(html_data, encoding='unicode')) + + content_between_tags = extr( + html_sample, '{"location":"/images/search/', 'advRsyaSearchColumn":null}}', default="fail" + ) + json_data = '{"location":"/images/search/' + content_between_tags + 'advRsyaSearchColumn":null}}' + + if content_between_tags == "fail": + content_between_tags = extr(html_sample, '{"location":"/images/search/', 'false}}}') + json_data = '{"location":"/images/search/' + content_between_tags + 'false}}}' + + json_resp = loads(json_data) + + results = [] + for _, item_data in json_resp['initialState']['serpList']['items']['entities'].items(): + title = item_data['snippet']['title'] + source = item_data['snippet']['url'] + thumb = item_data['image'] + fullsize_image = item_data['viewerData']['dups'][0]['url'] + height = item_data['viewerData']['dups'][0]['h'] + width = item_data['viewerData']['dups'][0]['w'] + filesize = item_data['viewerData']['dups'][0]['fileSizeInBytes'] + humanized_filesize = humanize_bytes(filesize) + + results.append( + { + 'title': title, + 'url': source, + 'img_src': fullsize_image, + 'filesize': humanized_filesize, + 'thumbnail_src': thumb, + 'template': 'images.html', + 'resolution': f'{width} x {height}', + } + ) + + return results + + return [] diff --git a/searx/infopage/fr/about.md b/searx/infopage/fr/about.md new file mode 100644 index 000000000..402f28069 --- /dev/null +++ b/searx/infopage/fr/about.md @@ -0,0 +1,87 @@ +# A propos de SearXNG + +SearXNG est un [Métamoteur] qui agrège les résultats d'autres +{{link('moteurs de recherche', 'preferences')}} tout en ne sauvegardant +aucune informations à propos de ses utilisateurs. + +Le projet SearXNG est maintenu par une communauté ouverte. +Rejoignez-nous sur Matrix si vous avez des questions ou simplement pour +discuter de SearXNG: [#searxng:matrix.org]. + +Aidez-nous à rendre SearXNG meilleur. + +- Vous pouvez améliorer les traductions de SearXNG avec l'outil + [Weblate]. +- Suivez le développement, contribuez au projet ou remontez des erreurs + en utilisant le [dépôt de sources]. +- Pour obtenir de plus amples informations, consultez la documentation + en ligne du [projet SearXNG]. + +## Pourquoi l'utiliser ? + +- SearXNG ne vous fournira pas de résultats aussi personnalisés que + Google, mais il ne générera pas non plus de suivi sur vous. +- SearXNG ne se soucis pas des recherches que vous faites, ne partage + aucune information avec des tiers et ne peut pas être utilisé contre + vous. +- SearXNG est un logiciel libre. Son code source est 100% ouvert et tout + le mode est encouragé à l'améliorer. + +Si vous êtes soucieux du respect de la vie privée et des libertés sur +Internet, faites de SearXNG votre moteur de recherche par défaut. Vous +pouvez aussi installer et utiliser SearXNG sur votre propre serveur. + +## Comment le configurer comme moteur de recherche par défaut ? + +SearXNG prend en charge [OpenSearch]. Pour plus d'informations sur la +manière de modifier votre moteur de recherche par défaut, veuillez +consulter la documentation de votre navigateur : + +- [Firefox] +- [Microsoft Edge] - Ce lien propose aussi les instructions pour les + navigateurs Chrome et Safari. +- Les navigateurs basés sur [Chromium] permettent d'ajouter des sites de + navigation sans même y accéder. + +Lorsqu'un moteur de recherche est ajouté, son nom doit être unique. Si +vous ne pouvez pas ajouter un moteur de recherche, veuillez : + +- Supprimer le doublon (le nom par défaut est SearXNG) ou bien +- Contacter le propriétaire de l'instance que vous souhaitez utiliser + afin qu'il modifie le nom de celle-ci. + +## Comment ça marche ? + +SearXNG est une reprise logicielle du projet [searx] [Métamoteur], +lui-même inspiré du [projet Seeks]. Il assure la confidentialité en +mélangeant vos recherches vers d'autres plateformes sans stocker aucune +données de recherche. SearXNG peut être ajouté à la barre de recherche +de votre navigateur et même être utilisé comme moteur de recherche par +défaut. + +Le lien "{{link('statistiques des moteurs', 'stats')}}" présente des +informations anonymisées concernant l'utilisation des divers moteurs de +recherche. + +## Comment reprendre la main ? + +SearXNG apprécie votre préoccupation concernant les traces de recherche. +N'hésitez pas à utiliser le [dépôt de sources] et à maintenir votre +propre instance de recherche. + +Ajouter votre instance à la [liste d'instances +publiques]({{get_setting('brand.public_instances')}}) afin d'aider +d'autres personnes à protéger leur vie privée et rendre l'Internet plus +libre. Plus Internet sera décentralisé, plus nous aurons de liberté ! + +[dépôt de sources]: {{GIT_URL}} +[#searxng:matrix.org]: https://matrix.to/#/#searxng:matrix.org +[projet SearXNG]: {{get_setting('brand.docs_url')}} +[searx]: https://github.com/searx/searx +[Métamoteur]: https://fr.wikipedia.org/wiki/M%C3%A9tamoteur +[Weblate]: https://translate.codeberg.org/projects/searxng/ +[projet Seeks]: https://beniz.github.io/seeks/ +[OpenSearch]: https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md +[Firefox]: https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox +[Microsoft Edge]: https://support.microsoft.com/en-us/help/4028574/microsoft-edge-change-the-default-search-engine +[Chromium]: https://www.chromium.org/tab-to-search diff --git a/searx/infopage/fr/search-syntax.md b/searx/infopage/fr/search-syntax.md new file mode 100644 index 000000000..15e678bb3 --- /dev/null +++ b/searx/infopage/fr/search-syntax.md @@ -0,0 +1,97 @@ +# Syntaxe de recherche + +SearXNG permet de modifier les catégories de recherche, les moteurs +utilisés ou encore la langue de recherche par l'intermédiaire d'une +syntaxe dédiée. La liste des moteurs de recherche, de catégories et de +langues disponibles est accessible depuis la page de +{{link('préférences', 'preferences')}}. + +## `!` Spécifier un moteur ou une catégorie + +Pour restreindre la recherche à un moteur ou une catégorie, utilisez le +caractère "!". Voici quelques exemples d'utilisation : + +- Rechercher **paris** sur Wikipédia. + + - {{search('!wp paris')}} + - {{search('!wikipedia paris')}} + +- Rechercher **paris** dans la catégorie **Carte**. + + - {{search('!map paris')}} + +- Rechercher des **Images**. + + - {{search('!images Wau Holland')}} + +Les abréviations de moteurs et de langues sont aussi valides. Il est +possible d'accumuler les moteurs et catégories dans une requête +complexe. Par exemple, {{search('!map !ddg !wp paris')}} recherchera +**paris** dans la catégorie **Carte** de DuckDuckGo et Wikipédia. + +## `:` Spécifier une langue + +Utilisez le préfixe ":" pour limiter la recherche à une langue en +particulier. Par exemple : + +- Rechercher dans les pages françaises de Wikipédia. + + - {{search(':fr !wp Wau Holland')}} + +## `!!` Recherches externes (!Bang) + +SearXNG supporte les recherches [DuckDuckGo] de type "!Bang". Utilisez +le préfixe "!!" pour être automatiquement redirigé vers un moteur de +recherche externe. Par exemple : + +- Rechercher sur Wikipédia en langue française. + + - {{search('!!wfr Wau Holland')}} + +Prenez garde au fait que de telles recherches sont exécutées directement +sur le moteur externe. Dans ce cas, SearXNG ne peut pas protéger votre +vie privée. + +[DuckDuckGo]: https://duckduckgo.com/bang + +## `!!` Redirection automatique + +En utilisant "!!" suivi d'un ou plusieurs espaces lors de votre +recherche, vous serez automatiquement redirigé vers le premier résultat +de recherche. Cela correspondant au fonctionnement "J'ai de la chance" +du moteur Google. Par exemple : + +- Rechercher et être redirigé directement vers le premier lien + correspondant. + + - {{search('!! Wau Holland')}} + +Prenez garde au fait qu'aucune vérification ne peut être faite +concernant le premier lien retourné. Il pourrait même s'agir d'un site +dangereux. Dans ce cas, SearXNG ne peut pas protéger votre vie +privée. Soyez prudent en utilisant cette fonctionnalité. + +## Requêtes spéciales + +Dans la section _requêtes spéciales_ de la page de {{link('préférences', +'preferences')}} se trouve une liste de mots clés à usage particulier. +Par exemple : + +- Générer une valeur aléatoire. + + - {{search('random uuid')}} + +- Calculer une moyenne. + + - {{search('avg 123 548 2.04 24.2')}} + +- Afficher la valeur de la variable _User-Agent_ utilisée par votre + navigateur (doit être activé manuellement). + + - {{search('user-agent')}} + +- Convertir une chaîne de caractères en valeurs de hachage ("hash digests") + (doit être activé manuellement). + + - {{search('md5 lorem ipsum')}} + - {{search('sha512 lorem ipsum')}} diff --git a/searx/plugins/hostnames.py b/searx/plugins/hostnames.py index 770b00e15..6519452db 100644 --- a/searx/plugins/hostnames.py +++ b/searx/plugins/hostnames.py @@ -1,7 +1,19 @@ # SPDX-License-Identifier: AGPL-3.0-or-later # pylint: disable=too-many-branches -"""In addition to rewriting/replace reslut URLs, the *hoostnames* plugin offers -other features. +""" +.. attention:: + + The **"Hostname replace"** plugin has been replace by **"Hostnames + plugin"**, see :pull:`3463` & :pull:`3552`. + +The **Hostnames plugin** can be enabled by adding it to the +``enabled_plugins`` **list** in the ``setting.yml`` like so. + + .. code:: yaml + + enabled_plugins: + - 'Hostnames plugin' + ... - ``hostnames.replace``: A **mapping** of regular expressions to hostnames to be replaced by other hostnames. diff --git a/searx/settings.yml b/searx/settings.yml index b6be0f701..0fe539162 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -1852,6 +1852,22 @@ engines: engine: unsplash shortcut: us + - name: yandex + engine: yandex + categories: general + search_type: web + shortcut: yd + disabled: true + inactive: true + + - name: yandex images + engine: yandex + categories: images + search_type: images + shortcut: ydi + disabled: true + inactive: true + - name: yandex music engine: yandex_music shortcut: ydm diff --git a/searx/translations/gl/LC_MESSAGES/messages.mo b/searx/translations/gl/LC_MESSAGES/messages.mo index 3fa7e5299..b413d207c 100644 Binary files a/searx/translations/gl/LC_MESSAGES/messages.mo and b/searx/translations/gl/LC_MESSAGES/messages.mo differ diff --git a/searx/translations/gl/LC_MESSAGES/messages.po b/searx/translations/gl/LC_MESSAGES/messages.po index 0f26c69a8..305186744 100644 --- a/searx/translations/gl/LC_MESSAGES/messages.po +++ b/searx/translations/gl/LC_MESSAGES/messages.po @@ -11,18 +11,19 @@ # return42 , 2024. msgid "" msgstr "" -"Project-Id-Version: searx\n" +"Project-Id-Version: searx\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2024-08-08 10:01+0000\n" -"PO-Revision-Date: 2024-07-30 08:18+0000\n" +"PO-Revision-Date: 2024-08-22 05:18+0000\n" "Last-Translator: ghose \n" +"Language-Team: Galician \n" "Language: gl\n" -"Language-Team: Galician " -"\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" "Generated-By: Babel 2.15.0\n" #. CONSTANT_NAMES['NO_SUBGROUPING'] @@ -1308,7 +1309,7 @@ msgid "" "multiple categories" msgstr "" "Buscar inmediatamente se hai unha categoría seleccionada. Desactiva para " -"elexir varias categorías" +"elixir varias categorías" #: searx/templates/simple/preferences/theme.html:2 msgid "Theme" @@ -1953,4 +1954,3 @@ msgstr "agochar vídeo" #~ msgid "Hostname replace" #~ msgstr "Substituír servidor" - diff --git a/searx/translations/sr/LC_MESSAGES/messages.mo b/searx/translations/sr/LC_MESSAGES/messages.mo index e9d4ddf5f..64dff5218 100644 Binary files a/searx/translations/sr/LC_MESSAGES/messages.mo and b/searx/translations/sr/LC_MESSAGES/messages.mo differ diff --git a/searx/translations/sr/LC_MESSAGES/messages.po b/searx/translations/sr/LC_MESSAGES/messages.po index be1bdbf21..b087ed3f2 100644 --- a/searx/translations/sr/LC_MESSAGES/messages.po +++ b/searx/translations/sr/LC_MESSAGES/messages.po @@ -10,22 +10,23 @@ # SecularSteve , 2022, 2023. # return42 , 2023. # return42 , 2024. +# crnobog , 2024. msgid "" msgstr "" -"Project-Id-Version: searx\n" +"Project-Id-Version: searx\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2024-08-08 10:01+0000\n" -"PO-Revision-Date: 2024-04-18 13:18+0000\n" -"Last-Translator: return42 " -"\n" +"PO-Revision-Date: 2024-08-21 01:18+0000\n" +"Last-Translator: crnobog \n" +"Language-Team: Serbian \n" "Language: sr\n" -"Language-Team: Serbian " -"\n" -"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " -"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 5.6.2\n" "Generated-By: Babel 2.15.0\n" #. CONSTANT_NAMES['NO_SUBGROUPING'] @@ -76,7 +77,7 @@ msgstr "радио" #. CATEGORY_NAMES['TV'] #: searx/searxng.msg msgid "tv" -msgstr "" +msgstr "телевизија" #. CATEGORY_NAMES['IT'] #: searx/searxng.msg @@ -176,62 +177,66 @@ msgstr "О нама" #. WEATHER_TERMS['AVERAGE TEMP.'] #: searx/searxng.msg msgid "Average temp." -msgstr "" +msgstr "Просечна температура" #. WEATHER_TERMS['CLOUD COVER'] #: searx/searxng.msg msgid "Cloud cover" -msgstr "" +msgstr "Облачност" #. WEATHER_TERMS['CONDITION'] #: searx/searxng.msg msgid "Condition" -msgstr "" +msgstr "Стање" #. WEATHER_TERMS['CURRENT CONDITION'] #: searx/searxng.msg msgid "Current condition" -msgstr "" +msgstr "Тренутно стање" #. WEATHER_TERMS['EVENING'] #: searx/engines/wttr.py:100 searx/searxng.msg +#, fuzzy msgid "Evening" -msgstr "Vece" +msgstr "Вече" #. WEATHER_TERMS['FEELS LIKE'] #: searx/searxng.msg msgid "Feels like" -msgstr "" +msgstr "Осећај" #. WEATHER_TERMS['HUMIDITY'] #: searx/searxng.msg msgid "Humidity" -msgstr "" +msgstr "Влажност" #. WEATHER_TERMS['MAX TEMP.'] #: searx/searxng.msg msgid "Max temp." -msgstr "" +msgstr "Највећа темп." #. WEATHER_TERMS['MIN TEMP.'] #: searx/searxng.msg msgid "Min temp." -msgstr "" +msgstr "Најмања темп." #. WEATHER_TERMS['MORNING'] #: searx/engines/wttr.py:100 searx/searxng.msg +#, fuzzy msgid "Morning" -msgstr "Jutro" +msgstr "Јутро" #. WEATHER_TERMS['NIGHT'] #: searx/engines/wttr.py:100 searx/searxng.msg +#, fuzzy msgid "Night" -msgstr "Noc" +msgstr "Ноћ" #. WEATHER_TERMS['NOON'] #: searx/engines/wttr.py:100 searx/searxng.msg +#, fuzzy msgid "Noon" -msgstr "Podne" +msgstr "Подне" #. WEATHER_TERMS['PRESSURE'] #: searx/searxng.msg @@ -1934,4 +1939,3 @@ msgstr "сакриј видео" #~ msgid "Hostname replace" #~ msgstr "Замени име хостинга" - diff --git a/tests/unit/test_engines_init.py b/tests/unit/test_engines_init.py index 4872a1b1b..e2445160a 100644 --- a/tests/unit/test_engines_init.py +++ b/tests/unit/test_engines_init.py @@ -10,6 +10,7 @@ class TestEnginesInit(SearxTestCase): # pylint: disable=missing-class-docstring def tearDownClass(cls): settings['outgoing']['using_tor_proxy'] = False settings['outgoing']['extra_proxy_timeout'] = 0 + engines.load_engines([]) def test_initialize_engines_default(self): engine_list = [ diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py index b85c90c68..a60089aef 100644 --- a/tests/unit/test_search.py +++ b/tests/unit/test_search.py @@ -2,6 +2,7 @@ # pylint: disable=missing-module-docstring, invalid-name from copy import copy +import logging import searx.search from searx.search import SearchQuery, EngineRef @@ -46,8 +47,13 @@ class SearchQueryTestCase(SearxTestCase): # pylint: disable=missing-class-docst class SearchTestCase(SearxTestCase): # pylint: disable=missing-class-docstring def setUp(self): + log = logging.getLogger("searx") + log_lev = log.level + log.setLevel(logging.ERROR) from searx import webapp # pylint: disable=import-outside-toplevel + log.setLevel(log_lev) + self.app = webapp.app @classmethod diff --git a/tests/unit/test_tineye.py b/tests/unit/test_tineye.py new file mode 100644 index 000000000..0530b4c5e --- /dev/null +++ b/tests/unit/test_tineye.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# pylint: disable=missing-module-docstring + + +from datetime import datetime +from unittest.mock import Mock +from requests import HTTPError +from searx.engines import load_engines, tineye +from tests import SearxTestCase + + +class TinEyeTests(SearxTestCase): # pylint: disable=missing-class-docstring + + def setUp(self): + load_engines([{'name': 'tineye', 'engine': 'tineye', 'shortcut': 'tin', 'timeout': 9.0, 'disabled': True}]) + + def tearDown(self): + load_engines([]) + + def test_status_code_raises(self): + response = Mock() + response.status_code = 401 + response.raise_for_status.side_effect = HTTPError() + self.assertRaises(HTTPError, lambda: tineye.response(response)) + + def test_returns_empty_list_for_422(self): + response = Mock() + response.json.return_value = {} + response.status_code = 422 + response.raise_for_status.side_effect = HTTPError() + with self.assertLogs(tineye.logger) as _dev_null: + results = tineye.response(response) + self.assertEqual(0, len(results)) + + def test_logs_format_for_422(self): + response = Mock() + response.json.return_value = {"suggestions": {"key": "Invalid image URL"}} + response.status_code = 422 + response.raise_for_status.side_effect = HTTPError() + + with self.assertLogs(tineye.logger) as assert_logs_context: + tineye.response(response) + self.assertIn(tineye.FORMAT_NOT_SUPPORTED, ','.join(assert_logs_context.output)) + + def test_logs_signature_for_422(self): + response = Mock() + response.json.return_value = {"suggestions": {"key": "NO_SIGNATURE_ERROR"}} + response.status_code = 422 + response.raise_for_status.side_effect = HTTPError() + + with self.assertLogs(tineye.logger) as assert_logs_context: + tineye.response(response) + self.assertIn(tineye.NO_SIGNATURE_ERROR, ','.join(assert_logs_context.output)) + + def test_logs_download_for_422(self): + response = Mock() + response.json.return_value = {"suggestions": {"key": "Download Error"}} + response.status_code = 422 + response.raise_for_status.side_effect = HTTPError() + + with self.assertLogs(tineye.logger) as assert_logs_context: + tineye.response(response) + self.assertIn(tineye.DOWNLOAD_ERROR, ','.join(assert_logs_context.output)) + + def test_empty_list_for_400(self): + response = Mock() + response.json.return_value = {} + response.status_code = 400 + response.raise_for_status.side_effect = HTTPError() + with self.assertLogs(tineye.logger) as _dev_null: + results = tineye.response(response) + self.assertEqual(0, len(results)) + + def test_logs_description_for_400(self): + description = 'There was a problem with that request. Error ID: ad5fc955-a934-43c1-8187-f9a61d301645' + response = Mock() + response.json.return_value = {"suggestions": {"description": [description], "title": "Oops! We're sorry!"}} + response.status_code = 400 + response.raise_for_status.side_effect = HTTPError() + + with self.assertLogs(tineye.logger) as assert_logs_context: + tineye.response(response) + self.assertIn(description, ','.join(assert_logs_context.output)) + + def test_crawl_date_parses(self): + date_str = '2020-05-25' + date = datetime.strptime(date_str, '%Y-%m-%d') + response = Mock() + response.json.return_value = { + 'matches': [ + { + 'backlinks': [ + { + 'crawl_date': date_str, + } + ] + } + ] + } + response.status_code = 200 + results = tineye.response(response) + self.assertEqual(date, results[0]['publishedDate']) diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py index 868645e17..7c6e1ef82 100644 --- a/tests/unit/test_webapp.py +++ b/tests/unit/test_webapp.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later # pylint: disable=missing-module-docstring +import logging import json from urllib.parse import ParseResult from mock import Mock @@ -20,8 +21,13 @@ class ViewsTestCase(SearxTestCase): # pylint: disable=missing-class-docstring, self.setattr4test(searx.search.processors, 'initialize_processor', dummy) + log = logging.getLogger("searx") + log_lev = log.level + log.setLevel(logging.ERROR) from searx import webapp # pylint: disable=import-outside-toplevel + log.setLevel(log_lev) + webapp.app.config['TESTING'] = True # to get better error messages self.app = webapp.app.test_client() diff --git a/utils/lib.sh b/utils/lib.sh index 16bfb4398..b932b875c 100755 --- a/utils/lib.sh +++ b/utils/lib.sh @@ -663,8 +663,8 @@ pyenv.install() { pyenv fi for i in ${PYOBJECTS}; do - build_msg PYENV "[install] pip install -e '$i${PY_SETUP_EXTRAS}'" - "${PY_ENV_BIN}/python" -m pip install -e "$i${PY_SETUP_EXTRAS}" + build_msg PYENV "[install] pip install --use-pep517 --no-build-isolation -e '$i${PY_SETUP_EXTRAS}'" + "${PY_ENV_BIN}/python" -m pip install --use-pep517 --no-build-isolation -e "$i${PY_SETUP_EXTRAS}" done fi pyenv.install.OK @@ -1674,7 +1674,7 @@ EOF } # apt packages -LXC_BASE_PACKAGES_debian="bash git build-essential python3 python3-venv" +LXC_BASE_PACKAGES_debian="bash git build-essential python3 python3-venv python-is-python3" # pacman packages LXC_BASE_PACKAGES_arch="bash git base-devel python" diff --git a/utils/lib_go.sh b/utils/lib_go.sh index 462bbbba0..0b58fad61 100755 --- a/utils/lib_go.sh +++ b/utils/lib_go.sh @@ -41,7 +41,7 @@ EOF } go.ls(){ - python3 <&1 | prefix_stdout "$_service_prefix" rm -rf "${SEARXNG_PYENV}" -python3 -m venv "${SEARXNG_PYENV}" +python -m venv "${SEARXNG_PYENV}" grep -qFs -- 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile \ || echo 'source ${SEARXNG_PYENV}/bin/activate' >> ~/.profile EOF @@ -505,7 +501,7 @@ pip install -U setuptools pip install -U wheel pip install -U pyyaml cd ${SEARXNG_SRC} -pip install -e . +pip install --use-pep517 --no-build-isolation -e . EOF } @@ -573,7 +569,7 @@ pip install -U pip pip install -U setuptools pip install -U wheel pip install -U pyyaml -pip install -U -e . +pip install -U --use-pep517 --no-build-isolation -e . EOF rst_para "update instance's settings.yml from ${SEARXNG_SETTINGS_PATH}" DEFAULT_SELECT=2 \