From d1b12f9016492143a4b3750c9fba89a7168fa179 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Sun, 8 Mar 2015 23:30:31 +0100 Subject: [PATCH 01/14] [mod] search return value --- searx/search.py | 27 ++++++++++++--------------- searx/webapp.py | 3 +-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/searx/search.py b/searx/search.py index 83163d1..862b17e 100644 --- a/searx/search.py +++ b/searx/search.py @@ -329,8 +329,8 @@ class Search(object): self.blocked_engines = get_blocked_engines(engines, request.cookies) self.results = [] - self.suggestions = [] - self.answers = [] + self.suggestions = set() + self.answers = set() self.infoboxes = [] self.request_data = {} @@ -429,9 +429,6 @@ class Search(object): requests = [] results_queue = Queue() results = {} - suggestions = set() - answers = set() - infoboxes = [] # increase number of searches number_of_searches += 1 @@ -511,7 +508,7 @@ class Search(object): selected_engine['name'])) if not requests: - return results, suggestions, answers, infoboxes + return self # send all search-request threaded_requests(requests) @@ -519,19 +516,19 @@ class Search(object): engine_name, engine_results = results_queue.get_nowait() # TODO type checks - [suggestions.add(x['suggestion']) + [self.suggestions.add(x['suggestion']) for x in list(engine_results) if 'suggestion' in x and engine_results.remove(x) is None] - [answers.add(x['answer']) + [self.answers.add(x['answer']) for x in list(engine_results) if 'answer' in x and engine_results.remove(x) is None] - infoboxes.extend(x for x in list(engine_results) - if 'infobox' in x - and engine_results.remove(x) is None) + self.infoboxes.extend(x for x in list(engine_results) + if 'infobox' in x + and engine_results.remove(x) is None) results[engine_name] = engine_results @@ -541,16 +538,16 @@ class Search(object): engines[engine_name].stats['result_count'] += len(engine_results) # score results and remove duplications - results = score_results(results) + self.results = score_results(results) # merge infoboxes according to their ids - infoboxes = merge_infoboxes(infoboxes) + self.infoboxes = merge_infoboxes(self.infoboxes) # update engine stats, using calculated score - for result in results: + for result in self.results: for res_engine in result['engines']: engines[result['engine']]\ .stats['score_count'] += result['score'] # return results, suggestions, answers and infoboxes - return results, suggestions, answers, infoboxes + return self diff --git a/searx/webapp.py b/searx/webapp.py index 13c965e..f71df79 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -323,8 +323,7 @@ def index(): 'index.html', ) - search.results, search.suggestions,\ - search.answers, search.infoboxes = search.search(request) + search.search(request) for result in search.results: From 8d1d4819ae53ff33a258e12ab6a2dc5b58e88846 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Mon, 9 Mar 2015 00:32:23 +0100 Subject: [PATCH 02/14] [fix] whitespace remove and proper no-result warning display --- searx/templates/oscar/results.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/searx/templates/oscar/results.html b/searx/templates/oscar/results.html index a758256..1551945 100644 --- a/searx/templates/oscar/results.html +++ b/searx/templates/oscar/results.html @@ -25,8 +25,8 @@ {% endif %} {% endfor %} - - {% if not results %} + + {% if not results and not answers %} {% include 'oscar/messages/no_results.html' %} {% endif %} @@ -82,7 +82,7 @@ {% for infobox in infoboxes %} {% include 'oscar/infobox.html' %} {% endfor %} - {% endif %} + {% endif %} {% if suggestions %}
@@ -111,7 +111,7 @@
- +
{% for output_type in ('csv', 'json', 'rss') %} @@ -122,7 +122,7 @@ - {% endfor %} + {% endfor %}
From 00cc4dcbf44d9ecea89befb08cae4ee5561c4247 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Tue, 10 Mar 2015 19:55:22 +0100 Subject: [PATCH 03/14] [enh] plugin support basics ++ self ip plugin --- searx/plugins/__init__.py | 46 +++++++++++++++++++++++++++++++++++++++ searx/plugins/self_ip.py | 17 +++++++++++++++ searx/webapp.py | 29 ++++++++++++++---------- 3 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 searx/plugins/__init__.py create mode 100644 searx/plugins/self_ip.py diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py new file mode 100644 index 0000000..b40d2ee --- /dev/null +++ b/searx/plugins/__init__.py @@ -0,0 +1,46 @@ +from searx.plugins import self_ip +from searx import logger +from sys import exit + +logger = logger.getChild('plugins') + +required_attrs = ('name', + 'description', + 'default_on') + + +class Plugin(): + default_on = False + name = 'Default plugin' + + +class PluginStore(): + + def __init__(self): + self.plugins = [] + + def __iter__(self): + for plugin in plugins: + yield plugin + + def register(self, *plugins): + for plugin in plugins: + for plugin_attr in required_attrs: + if not hasattr(plugin, plugin_attr): + logger.critical('missing attribute "{0}", cannot load plugin: {1}'.format(plugin_attr, plugin)) + exit(3) + self.plugins.append(plugin) + + def call(self, plugin_type, request, *args, **kwargs): + ret = True + for plugin in self.plugins: + if hasattr(plugin, plugin_type): + ret = getattr(plugin, plugin_type)(request, *args, **kwargs) + if not ret: + break + + return ret + + +plugins = PluginStore() +plugins.register(self_ip) diff --git a/searx/plugins/self_ip.py b/searx/plugins/self_ip.py new file mode 100644 index 0000000..0db6c08 --- /dev/null +++ b/searx/plugins/self_ip.py @@ -0,0 +1,17 @@ + +name = "Self IP" +description = "" +default_on = True + + +def pre_search(request, ctx): + if ctx['search'].query == 'ip': + x_forwarded_for = request.headers.getlist("X-Forwarded-For") + if x_forwarded_for: + ip = x_forwarded_for[0] + else: + ip = request.remote_addr + ctx['search'].answers.clear() + ctx['search'].answers.add(ip) + return False + return True diff --git a/searx/webapp.py b/searx/webapp.py index f71df79..f5d779f 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -27,6 +27,18 @@ import cStringIO import os import hashlib +from searx import logger +logger = logger.getChild('webapp') + +try: + from pygments import highlight + from pygments.lexers import get_lexer_by_name + from pygments.formatters import HtmlFormatter +except: + logger.critical("cannot import dependency: pygments") + from sys import exit + exit(1) + from datetime import datetime, timedelta from urllib import urlencode from werkzeug.contrib.fixers import ProxyFix @@ -51,19 +63,9 @@ from searx.https_rewrite import https_url_rewrite from searx.search import Search from searx.query import Query from searx.autocomplete import searx_bang, backends as autocomplete_backends -from searx import logger -try: - from pygments import highlight - from pygments.lexers import get_lexer_by_name - from pygments.formatters import HtmlFormatter -except: - logger.critical("cannot import dependency: pygments") - from sys import exit - exit(1) +from searx.plugins import plugins -logger = logger.getChild('webapp') - static_path, templates_path, themes =\ get_themes(settings['themes_path'] if settings.get('themes_path') @@ -323,7 +325,10 @@ def index(): 'index.html', ) - search.search(request) + if plugins.call('pre_search', request, locals()): + search.search(request) + + plugins.call('post_search', request, locals()) for result in search.results: From cae22bfc7609d00f987e679cf6d048873d268d84 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Tue, 10 Mar 2015 20:44:02 +0100 Subject: [PATCH 04/14] [enh] per user plugin switch --- searx/plugins/__init__.py | 4 ++-- searx/webapp.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py index b40d2ee..9c9c52a 100644 --- a/searx/plugins/__init__.py +++ b/searx/plugins/__init__.py @@ -20,7 +20,7 @@ class PluginStore(): self.plugins = [] def __iter__(self): - for plugin in plugins: + for plugin in self.plugins: yield plugin def register(self, *plugins): @@ -33,7 +33,7 @@ class PluginStore(): def call(self, plugin_type, request, *args, **kwargs): ret = True - for plugin in self.plugins: + for plugin in request.user_plugins: if hasattr(plugin, plugin_type): ret = getattr(plugin, plugin_type)(request, *args, **kwargs) if not ret: diff --git a/searx/webapp.py b/searx/webapp.py index f5d779f..78c0c71 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -305,6 +305,18 @@ def render(template_name, override_theme=None, **kwargs): '{}/{}'.format(kwargs['theme'], template_name), **kwargs) +@app.before_request +def pre_request(): + + request.user_plugins = [] + allowed_plugins = request.cookies.get('allowed_plugins', '').split(',') + disabled_plugins = request.cookies.get('disabled_plugins', '').split(',') + for plugin in plugins: + if ((plugin.default_on and plugin.name not in disabled_plugins) + or plugin.name in allowed_plugins): + request.user_plugins.append(plugin) + + @app.route('/search', methods=['GET', 'POST']) @app.route('/', methods=['GET', 'POST']) def index(): From 88aee611f7c016d1f05202af9d9f647c8e5d26e2 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Tue, 10 Mar 2015 22:45:59 +0100 Subject: [PATCH 05/14] [mod] merge GET, POST vars --- searx/webapp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/searx/webapp.py b/searx/webapp.py index 78c0c71..d2985c2 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -307,6 +307,11 @@ def render(template_name, override_theme=None, **kwargs): @app.before_request def pre_request(): + # merge GET, POST vars + request.form = dict(request.form.items()) + for k, v in request.args: + if k not in request.form: + request.form[k] = v request.user_plugins = [] allowed_plugins = request.cookies.get('allowed_plugins', '').split(',') @@ -507,7 +512,6 @@ def preferences(): autocomplete = '' method = 'POST' safesearch = '1' - for pd_name, pd in request.form.items(): if pd_name.startswith('category_'): category = pd_name[9:] From 359dfc5ebb011f8c241fbfa16ec34fcbec844064 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Tue, 10 Mar 2015 23:03:06 +0100 Subject: [PATCH 06/14] [mod] checkbox macro --- searx/templates/oscar/macros.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html index 1ba1617..feb9ba9 100644 --- a/searx/templates/oscar/macros.html +++ b/searx/templates/oscar/macros.html @@ -59,3 +59,11 @@ {% endif %} {%- endmacro %} + +{% macro checkbox_toggle(id, blocked) -%} +
+ + + +
+{%- endmacro %} From 9cb66be29c287357e9b7135fe8840aa8b5979972 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Wed, 11 Mar 2015 01:42:25 +0100 Subject: [PATCH 07/14] [enh] plugin id --- searx/plugins/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py index 9c9c52a..61ec211 100644 --- a/searx/plugins/__init__.py +++ b/searx/plugins/__init__.py @@ -29,6 +29,7 @@ class PluginStore(): if not hasattr(plugin, plugin_attr): logger.critical('missing attribute "{0}", cannot load plugin: {1}'.format(plugin_attr, plugin)) exit(3) + plugin.id = plugin.name.replace(' ', '_') self.plugins.append(plugin) def call(self, plugin_type, request, *args, **kwargs): From 9eeb36c787f012f525cbe9c53495a7542bd9eb16 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Wed, 11 Mar 2015 01:44:33 +0100 Subject: [PATCH 08/14] [enh] selfip plugin description --- searx/plugins/self_ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/searx/plugins/self_ip.py b/searx/plugins/self_ip.py index 0db6c08..0aec996 100644 --- a/searx/plugins/self_ip.py +++ b/searx/plugins/self_ip.py @@ -1,6 +1,6 @@ name = "Self IP" -description = "" +description = "Display your source IP address" default_on = True From 37c58fd9caefe67d0c650ea1e900ad83e78e8c1a Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Wed, 11 Mar 2015 18:57:36 +0100 Subject: [PATCH 09/14] [enh] plugin preferences - server-side ++ oscar theme --- searx/templates/oscar/preferences.html | 31 +++++++++++++++++++++----- searx/webapp.py | 29 +++++++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/searx/templates/oscar/preferences.html b/searx/templates/oscar/preferences.html index 126bdbd..65b7f4b 100644 --- a/searx/templates/oscar/preferences.html +++ b/searx/templates/oscar/preferences.html @@ -1,4 +1,4 @@ -{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl %} +{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle %} {% extends "oscar/base.html" %} {% block title %}{{ _('preferences') }} - {% endblock %} {% block site_alert_warning_nojs %} @@ -16,6 +16,7 @@ @@ -139,11 +140,7 @@
{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})
{% endif %}
-
- - - -
+ {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in blocked_engines) }}
{% if rtl %}
{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})‎
@@ -157,6 +154,28 @@ {% endfor %} +
+ +
+
+ {% for plugin in plugins %} +
+
+

{{ plugin.name }}

+
+
+
{{ plugin.description }}
+
+ {{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }} +
+
+
+ {% endfor %} +
+
+

{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
diff --git a/searx/webapp.py b/searx/webapp.py index d2985c2..e3c372e 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -317,8 +317,8 @@ def pre_request(): allowed_plugins = request.cookies.get('allowed_plugins', '').split(',') disabled_plugins = request.cookies.get('disabled_plugins', '').split(',') for plugin in plugins: - if ((plugin.default_on and plugin.name not in disabled_plugins) - or plugin.name in allowed_plugins): + if ((plugin.default_on and plugin.id not in disabled_plugins) + or plugin.id in allowed_plugins): request.user_plugins.append(plugin) @@ -508,6 +508,7 @@ def preferences(): blocked_engines = get_blocked_engines(engines, request.cookies) else: # on save selected_categories = [] + post_disabled_plugins = [] locale = None autocomplete = '' method = 'POST' @@ -534,14 +535,34 @@ def preferences(): safesearch = pd elif pd_name.startswith('engine_'): if pd_name.find('__') > -1: - engine_name, category = pd_name.replace('engine_', '', 1).split('__', 1) + # TODO fix underscore vs space + engine_name, category = [x.replace('_', ' ') for x in + pd_name.replace('engine_', '', 1).split('__', 1)] if engine_name in engines and category in engines[engine_name].categories: blocked_engines.append((engine_name, category)) elif pd_name == 'theme': theme = pd if pd in themes else default_theme + elif pd_name.startswith('plugin_'): + plugin_id = pd_name.replace('plugin_', '', 1) + if not any(plugin.id == plugin_id for plugin in plugins): + continue + post_disabled_plugins.append(plugin_id) else: resp.set_cookie(pd_name, pd, max_age=cookie_max_age) + disabled_plugins = [] + allowed_plugins = [] + for plugin in plugins: + if plugin.default_on: + if plugin.id in post_disabled_plugins: + disabled_plugins.append(plugin.id) + elif plugin.id not in post_disabled_plugins: + allowed_plugins.append(plugin.id) + + resp.set_cookie('disabled_plugins', ','.join(disabled_plugins), max_age=cookie_max_age) + + resp.set_cookie('allowed_plugins', ','.join(allowed_plugins), max_age=cookie_max_age) + resp.set_cookie( 'blocked_engines', ','.join('__'.join(e) for e in blocked_engines), max_age=cookie_max_age @@ -591,6 +612,8 @@ def preferences(): autocomplete_backends=autocomplete_backends, shortcuts={y: x for x, y in engine_shortcuts.items()}, themes=themes, + plugins=plugins, + allowed_plugins=[plugin.id for plugin in request.user_plugins], theme=get_current_theme_name()) From 80ba6f22fafb24c97c5df80e22d913aaefbda644 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Wed, 11 Mar 2015 21:23:28 +0100 Subject: [PATCH 10/14] [fix] webapp tests --- searx/tests/test_webapp.py | 42 ++++++++------------------------------ 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/searx/tests/test_webapp.py b/searx/tests/test_webapp.py index 8bbe5d0..32eff5f 100644 --- a/searx/tests/test_webapp.py +++ b/searx/tests/test_webapp.py @@ -2,7 +2,6 @@ import json from urlparse import ParseResult -from mock import patch from searx import webapp from searx.testing import SearxTestCase @@ -33,6 +32,11 @@ class ViewsTestCase(SearxTestCase): }, ] + def search_mock(search_self, *args): + search_self.results = self.test_results + + webapp.Search.search = search_mock + self.maxDiff = None # to see full diffs def test_index_empty(self): @@ -40,14 +44,7 @@ class ViewsTestCase(SearxTestCase): self.assertEqual(result.status_code, 200) self.assertIn('

searx

', result.data) - @patch('searx.search.Search.search') - def test_index_html(self, search): - search.return_value = ( - self.test_results, - set(), - set(), - set() - ) + def test_index_html(self): result = self.app.post('/', data={'q': 'test'}) self.assertIn( '

youtubeSecond Test

', # noqa @@ -58,14 +55,7 @@ class ViewsTestCase(SearxTestCase): result.data ) - @patch('searx.search.Search.search') - def test_index_json(self, search): - search.return_value = ( - self.test_results, - set(), - set(), - set() - ) + def test_index_json(self): result = self.app.post('/', data={'q': 'test', 'format': 'json'}) result_dict = json.loads(result.data) @@ -76,14 +66,7 @@ class ViewsTestCase(SearxTestCase): self.assertEqual( result_dict['results'][0]['url'], 'http://first.test.xyz') - @patch('searx.search.Search.search') - def test_index_csv(self, search): - search.return_value = ( - self.test_results, - set(), - set(), - set() - ) + def test_index_csv(self): result = self.app.post('/', data={'q': 'test', 'format': 'csv'}) self.assertEqual( @@ -93,14 +76,7 @@ class ViewsTestCase(SearxTestCase): result.data ) - @patch('searx.search.Search.search') - def test_index_rss(self, search): - search.return_value = ( - self.test_results, - set(), - set(), - set() - ) + def test_index_rss(self): result = self.app.post('/', data={'q': 'test', 'format': 'rss'}) self.assertIn( From 13ea0a20ae222c51d7aac6f751124e484bc1cae3 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Sat, 14 Mar 2015 19:45:15 +0100 Subject: [PATCH 11/14] [enh] gettext in self ip plugin --- searx/plugins/self_ip.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/searx/plugins/self_ip.py b/searx/plugins/self_ip.py index 0aec996..0353be7 100644 --- a/searx/plugins/self_ip.py +++ b/searx/plugins/self_ip.py @@ -1,9 +1,12 @@ - +from flask.ext.babel import gettext name = "Self IP" -description = "Display your source IP address" +description = gettext('Display your source IP address if the query expression is "ip"') default_on = True +# attach callback to the pre search hook +# request: flask request object +# ctx: the whole local context of the pre search hook def pre_search(request, ctx): if ctx['search'].query == 'ip': x_forwarded_for = request.headers.getlist("X-Forwarded-For") @@ -13,5 +16,6 @@ def pre_search(request, ctx): ip = request.remote_addr ctx['search'].answers.clear() ctx['search'].answers.add(ip) + # return False prevents exeecution of the original block return False return True From f7c18a04ac9d21691f9035ae0ee5e1b0e82dc33e Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Sat, 14 Mar 2015 19:45:39 +0100 Subject: [PATCH 12/14] [mod] disable gigablast by default --- searx/settings.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/searx/settings.yml b/searx/settings.yml index b2689bd..68625fe 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -106,6 +106,7 @@ engines: - name : gigablast engine : gigablast shortcut : gb + disabled: True - name : github engine : github From bf5d6f56c66feb3cac760f0f30cf4585a2e6134e Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Sat, 14 Mar 2015 20:22:26 +0100 Subject: [PATCH 13/14] [enh] plugin attribute type check --- searx/plugins/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py index 61ec211..1cc2325 100644 --- a/searx/plugins/__init__.py +++ b/searx/plugins/__init__.py @@ -4,14 +4,15 @@ from sys import exit logger = logger.getChild('plugins') -required_attrs = ('name', - 'description', - 'default_on') +required_attrs = (('name', str), + ('description', str), + ('default_on', bool)) class Plugin(): default_on = False name = 'Default plugin' + description = 'Default plugin description' class PluginStore(): @@ -25,8 +26,8 @@ class PluginStore(): def register(self, *plugins): for plugin in plugins: - for plugin_attr in required_attrs: - if not hasattr(plugin, plugin_attr): + for plugin_attr, plugin_attr_type in required_attrs: + if not hasattr(plugin, plugin_attr) or not isinstance(getattr(plugin, plugin_attr), plugin_attr_type): logger.critical('missing attribute "{0}", cannot load plugin: {1}'.format(plugin_attr, plugin)) exit(3) plugin.id = plugin.name.replace(' ', '_') From f57149f912eece7dab1069e078f6bfe54ffd46e1 Mon Sep 17 00:00:00 2001 From: Adam Tauber Date: Sat, 14 Mar 2015 22:35:29 +0100 Subject: [PATCH 14/14] [enh] plugin tests --- searx/tests/test_plugins.py | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 searx/tests/test_plugins.py diff --git a/searx/tests/test_plugins.py b/searx/tests/test_plugins.py new file mode 100644 index 0000000..19a02c7 --- /dev/null +++ b/searx/tests/test_plugins.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +from searx.testing import SearxTestCase +from searx import plugins +from mock import Mock + + +class PluginStoreTest(SearxTestCase): + + def test_PluginStore_init(self): + store = plugins.PluginStore() + self.assertTrue(isinstance(store.plugins, list) and len(store.plugins) == 0) + + def test_PluginStore_register(self): + store = plugins.PluginStore() + testplugin = plugins.Plugin() + store.register(testplugin) + + self.assertTrue(len(store.plugins) == 1) + + def test_PluginStore_call(self): + store = plugins.PluginStore() + testplugin = plugins.Plugin() + store.register(testplugin) + setattr(testplugin, 'asdf', Mock()) + request = Mock(user_plugins=[]) + store.call('asdf', request, Mock()) + + self.assertFalse(testplugin.asdf.called) + + request.user_plugins.append(testplugin) + store.call('asdf', request, Mock()) + + self.assertTrue(testplugin.asdf.called) + + +class SelfIPTest(SearxTestCase): + + def test_PluginStore_init(self): + store = plugins.PluginStore() + store.register(plugins.self_ip) + + self.assertTrue(len(store.plugins) == 1) + + request = Mock(user_plugins=store.plugins, + remote_addr='127.0.0.1') + request.headers.getlist.return_value = [] + ctx = {'search': Mock(answers=set(), + query='ip')} + store.call('pre_search', request, ctx) + self.assertTrue('127.0.0.1' in ctx['search'].answers)