Explorar el Código

Clean up the architecture

Purposes :
- isolate the plugins calls
- distinction between parsing the web request and running the search (Search class). To be able to test code easily, to run searx code outside a web server, to filter the search query parameters with plugins more easily, etc...

Details :
- request.request_data contains request.form or request.args (initialize inside pre_request() function)
- Query class is renamed RawTextQuery
- SearchQuery class defines all search parameters
- get_search_query_from_webapp create a SearchQuery instance (basically the previous Search.__init__ code)
- Search class and SearchWithPlugins class takes a SearchQuery instance as class constructor parameter
- SearchWithPlugins class inherites from Search class, and run plugins
- A dedicated function search_with_plugins executes plugins to have a well define locals() (which is used by the plugins code).
- All plugins code is executed inside the try...except block (webapp.py, index function)
- advanced_search HTTP parameter value stays in webapp.py (it is only part of UI)
- multiple calls to result_container.get_ordered_results() doesn't compute the order multiple time (note : this method was call only once before)
- paging value is stored in the result_container class (compute in the extend method)
- test about engine.suspend_end_time is done during search method call (instead of __init__)
- check that the format parameter value is one of these : html, rss, json, rss (before the html value was assumed but some text formatting wasn't not done)
dalf hace 8 años
padre
commit
67e11c42b9
Se han modificado 6 ficheros con 266 adiciones y 196 borrados
  1. 4
    4
      searx/plugins/self_info.py
  2. 18
    2
      searx/query.py
  3. 14
    3
      searx/results.py
  4. 163
    127
      searx/search.py
  5. 57
    49
      searx/webapp.py
  6. 10
    11
      tests/unit/test_plugins.py

+ 4
- 4
searx/plugins/self_info.py Ver fichero

37
             ip = x_forwarded_for[0]
37
             ip = x_forwarded_for[0]
38
         else:
38
         else:
39
             ip = request.remote_addr
39
             ip = request.remote_addr
40
-        ctx['search'].result_container.answers.clear()
41
-        ctx['search'].result_container.answers.add(ip)
40
+        ctx['result_container'].answers.clear()
41
+        ctx['result_container'].answers.add(ip)
42
     elif p.match(ctx['search'].query):
42
     elif p.match(ctx['search'].query):
43
         ua = request.user_agent
43
         ua = request.user_agent
44
-        ctx['search'].result_container.answers.clear()
45
-        ctx['search'].result_container.answers.add(ua)
44
+        ctx['result_container'].answers.clear()
45
+        ctx['result_container'].answers.add(ua)
46
     return True
46
     return True

+ 18
- 2
searx/query.py Ver fichero

25
 import re
25
 import re
26
 
26
 
27
 
27
 
28
-class Query(object):
29
-    """parse query"""
28
+class RawTextQuery(object):
29
+    """parse raw text query (the value from the html input)"""
30
 
30
 
31
     def __init__(self, query, disabled_engines):
31
     def __init__(self, query, disabled_engines):
32
         self.query = query
32
         self.query = query
130
     def getFullQuery(self):
130
     def getFullQuery(self):
131
         # get full querry including whitespaces
131
         # get full querry including whitespaces
132
         return string.join(self.query_parts, '')
132
         return string.join(self.query_parts, '')
133
+
134
+
135
+class SearchQuery(object):
136
+    """container for all the search parameters (query, language, etc...)"""
137
+
138
+    def __init__(self, query, engines, categories, lang, safesearch, pageno, time_range):
139
+        self.query = query
140
+        self.engines = engines
141
+        self.categories = categories
142
+        self.lang = lang
143
+        self.safesearch = safesearch
144
+        self.pageno = pageno
145
+        self.time_range = time_range
146
+
147
+    def __str__(self):
148
+        return str(self.query) + ";" + str(self.engines)

+ 14
- 3
searx/results.py Ver fichero

128
         self.suggestions = set()
128
         self.suggestions = set()
129
         self.answers = set()
129
         self.answers = set()
130
         self._number_of_results = []
130
         self._number_of_results = []
131
+        self._ordered = False
132
+        self.paging = False
131
 
133
 
132
     def extend(self, engine_name, results):
134
     def extend(self, engine_name, results):
133
         for result in list(results):
135
         for result in list(results):
153
 
155
 
154
         self.results[engine_name].extend(results)
156
         self.results[engine_name].extend(results)
155
 
157
 
158
+        if not self.paging and engines[engine_name].paging:
159
+            self.paging = True
160
+
156
         for i, result in enumerate(results):
161
         for i, result in enumerate(results):
157
             try:
162
             try:
158
                 result['url'] = result['url'].decode('utf-8')
163
                 result['url'] = result['url'].decode('utf-8')
219
             with RLock():
224
             with RLock():
220
                 self._merged_results.append(result)
225
                 self._merged_results.append(result)
221
 
226
 
222
-    def get_ordered_results(self):
227
+    def order_results(self):
223
         for result in self._merged_results:
228
         for result in self._merged_results:
224
             score = result_score(result)
229
             score = result_score(result)
225
             result['score'] = score
230
             result['score'] = score
269
                 # update categoryIndex
274
                 # update categoryIndex
270
                 categoryPositions[category] = {'index': len(gresults), 'count': 8}
275
                 categoryPositions[category] = {'index': len(gresults), 'count': 8}
271
 
276
 
272
-        # return gresults
273
-        return gresults
277
+        # update _merged_results
278
+        self._ordered = True
279
+        self._merged_results = gresults
280
+
281
+    def get_ordered_results(self):
282
+        if not self._ordered:
283
+            self.order_results()
284
+        return self._merged_results
274
 
285
 
275
     def results_length(self):
286
     def results_length(self):
276
         return len(self._merged_results)
287
         return len(self._merged_results)

+ 163
- 127
searx/search.py Ver fichero

25
     categories, engines
25
     categories, engines
26
 )
26
 )
27
 from searx.utils import gen_useragent
27
 from searx.utils import gen_useragent
28
-from searx.query import Query
28
+from searx.query import RawTextQuery, SearchQuery
29
 from searx.results import ResultContainer
29
 from searx.results import ResultContainer
30
 from searx import logger
30
 from searx import logger
31
+from searx.plugins import plugins
31
 
32
 
32
 logger = logger.getChild('search')
33
 logger = logger.getChild('search')
33
 
34
 
127
     return process_callback
128
     return process_callback
128
 
129
 
129
 
130
 
130
-class Search(object):
131
-
132
-    """Search information container"""
133
-
134
-    def __init__(self, request):
135
-        # init vars
136
-        super(Search, self).__init__()
137
-        self.query = None
138
-        self.engines = []
139
-        self.categories = []
140
-        self.paging = False
141
-        self.pageno = 1
142
-        self.lang = 'all'
143
-        self.time_range = None
144
-        self.is_advanced = None
145
-
146
-        # set blocked engines
147
-        self.disabled_engines = request.preferences.engines.get_disabled()
148
-
149
-        self.result_container = ResultContainer()
150
-        self.request_data = {}
151
-
152
-        # set specific language if set
153
-        self.lang = request.preferences.get_value('language')
154
-
155
-        # set request method
156
-        if request.method == 'POST':
157
-            self.request_data = request.form
131
+def get_search_query_from_webapp(preferences, request_data):
132
+    query = None
133
+    query_engines = []
134
+    query_categories = []
135
+    query_paging = False
136
+    query_pageno = 1
137
+    query_lang = 'all'
138
+    query_time_range = None
139
+
140
+    # set blocked engines
141
+    disabled_engines = preferences.engines.get_disabled()
142
+
143
+    # set specific language if set
144
+    query_lang = preferences.get_value('language')
145
+
146
+    # safesearch
147
+    query_safesearch = preferences.get_value('safesearch')
148
+
149
+    # TODO better exceptions
150
+    if not request_data.get('q'):
151
+        raise Exception('noquery')
152
+
153
+    # set pagenumber
154
+    pageno_param = request_data.get('pageno', '1')
155
+    if not pageno_param.isdigit() or int(pageno_param) < 1:
156
+        pageno_param = 1
157
+
158
+    query_pageno = int(pageno_param)
159
+
160
+    # parse query, if tags are set, which change
161
+    # the serch engine or search-language
162
+    raw_text_query = RawTextQuery(request_data['q'], disabled_engines)
163
+    raw_text_query.parse_query()
164
+
165
+    # set query
166
+    query = raw_text_query.getSearchQuery()
167
+
168
+    # get last selected language in query, if possible
169
+    # TODO support search with multible languages
170
+    if len(raw_text_query.languages):
171
+        query_lang = raw_text_query.languages[-1]
172
+
173
+    query_time_range = request_data.get('time_range')
174
+
175
+    query_engines = raw_text_query.engines
176
+
177
+    # if engines are calculated from query,
178
+    # set categories by using that informations
179
+    if query_engines and raw_text_query.specific:
180
+        query_categories = list(set(engine['category']
181
+                                    for engine in query_engines))
182
+
183
+    # otherwise, using defined categories to
184
+    # calculate which engines should be used
185
+    else:
186
+        # set categories/engines
187
+        load_default_categories = True
188
+        for pd_name, pd in request_data.items():
189
+            if pd_name == 'categories':
190
+                query_categories.extend(categ for categ in map(unicode.strip, pd.split(',')) if categ in categories)
191
+            elif pd_name == 'engines':
192
+                pd_engines = [{'category': engines[engine].categories[0],
193
+                               'name': engine}
194
+                              for engine in map(unicode.strip, pd.split(',')) if engine in engines]
195
+                if pd_engines:
196
+                    query_engines.extend(pd_engines)
197
+                    load_default_categories = False
198
+            elif pd_name.startswith('category_'):
199
+                category = pd_name[9:]
200
+
201
+                # if category is not found in list, skip
202
+                if category not in categories:
203
+                    continue
204
+
205
+                if pd != 'off':
206
+                    # add category to list
207
+                    query_categories.append(category)
208
+                elif category in query_categories:
209
+                    # remove category from list if property is set to 'off'
210
+                    query_categories.remove(category)
211
+
212
+        if not load_default_categories:
213
+            if not query_categories:
214
+                query_categories = list(set(engine['category']
215
+                                            for engine in engines))
158
         else:
216
         else:
159
-            self.request_data = request.args
160
-
161
-        # TODO better exceptions
162
-        if not self.request_data.get('q'):
163
-            raise Exception('noquery')
164
-
165
-        # set pagenumber
166
-        pageno_param = self.request_data.get('pageno', '1')
167
-        if not pageno_param.isdigit() or int(pageno_param) < 1:
168
-            pageno_param = 1
169
-
170
-        self.pageno = int(pageno_param)
171
-
172
-        # parse query, if tags are set, which change
173
-        # the serch engine or search-language
174
-        query_obj = Query(self.request_data['q'], self.disabled_engines)
175
-        query_obj.parse_query()
176
-
177
-        # set query
178
-        self.query = query_obj.getSearchQuery()
179
-
180
-        # get last selected language in query, if possible
181
-        # TODO support search with multible languages
182
-        if len(query_obj.languages):
183
-            self.lang = query_obj.languages[-1]
184
-
185
-        self.time_range = self.request_data.get('time_range')
186
-        self.is_advanced = self.request_data.get('advanced_search')
187
-
188
-        self.engines = query_obj.engines
189
-
190
-        # if engines are calculated from query,
191
-        # set categories by using that informations
192
-        if self.engines and query_obj.specific:
193
-            self.categories = list(set(engine['category']
194
-                                       for engine in self.engines))
195
-
196
-        # otherwise, using defined categories to
197
-        # calculate which engines should be used
198
-        else:
199
-            # set categories/engines
200
-            load_default_categories = True
201
-            for pd_name, pd in self.request_data.items():
202
-                if pd_name == 'categories':
203
-                    self.categories.extend(categ for categ in map(unicode.strip, pd.split(',')) if categ in categories)
204
-                elif pd_name == 'engines':
205
-                    pd_engines = [{'category': engines[engine].categories[0],
206
-                                   'name': engine}
207
-                                  for engine in map(unicode.strip, pd.split(',')) if engine in engines]
208
-                    if pd_engines:
209
-                        self.engines.extend(pd_engines)
210
-                        load_default_categories = False
211
-                elif pd_name.startswith('category_'):
212
-                    category = pd_name[9:]
213
-
214
-                    # if category is not found in list, skip
215
-                    if category not in categories:
216
-                        continue
217
-
218
-                    if pd != 'off':
219
-                        # add category to list
220
-                        self.categories.append(category)
221
-                    elif category in self.categories:
222
-                        # remove category from list if property is set to 'off'
223
-                        self.categories.remove(category)
224
-
225
-            if not load_default_categories:
226
-                if not self.categories:
227
-                    self.categories = list(set(engine['category']
228
-                                               for engine in self.engines))
229
-                return
230
-
231
             # if no category is specified for this search,
217
             # if no category is specified for this search,
232
             # using user-defined default-configuration which
218
             # using user-defined default-configuration which
233
             # (is stored in cookie)
219
             # (is stored in cookie)
234
-            if not self.categories:
235
-                cookie_categories = request.preferences.get_value('categories')
220
+            if not query_categories:
221
+                cookie_categories = preferences.get_value('categories')
236
                 for ccateg in cookie_categories:
222
                 for ccateg in cookie_categories:
237
                     if ccateg in categories:
223
                     if ccateg in categories:
238
-                        self.categories.append(ccateg)
224
+                        query_categories.append(ccateg)
239
 
225
 
240
             # if still no category is specified, using general
226
             # if still no category is specified, using general
241
             # as default-category
227
             # as default-category
242
-            if not self.categories:
243
-                self.categories = ['general']
228
+            if not query_categories:
229
+                query_categories = ['general']
244
 
230
 
245
             # using all engines for that search, which are
231
             # using all engines for that search, which are
246
             # declared under the specific categories
232
             # declared under the specific categories
247
-            for categ in self.categories:
248
-                self.engines.extend({'category': categ,
249
-                                     'name': engine.name}
250
-                                    for engine in categories[categ]
251
-                                    if (engine.name, categ) not in self.disabled_engines)
233
+            for categ in query_categories:
234
+                query_engines.extend({'category': categ,
235
+                                      'name': engine.name}
236
+                                     for engine in categories[categ]
237
+                                     if (engine.name, categ) not in disabled_engines)
238
+
239
+    return SearchQuery(query, query_engines, query_categories,
240
+                       query_lang, query_safesearch, query_pageno, query_time_range)
252
 
241
 
253
-        # remove suspended engines
254
-        self.engines = [e for e in self.engines
255
-                        if engines[e['name']].suspend_end_time <= time()]
242
+
243
+class Search(object):
244
+
245
+    """Search information container"""
246
+
247
+    def __init__(self, search_query):
248
+        # init vars
249
+        super(Search, self).__init__()
250
+        self.search_query = search_query
251
+        self.result_container = ResultContainer()
256
 
252
 
257
     # do search-request
253
     # do search-request
258
-    def search(self, request):
254
+    def search(self):
259
         global number_of_searches
255
         global number_of_searches
260
 
256
 
261
         # init vars
257
         # init vars
268
         # user_agent = request.headers.get('User-Agent', '')
264
         # user_agent = request.headers.get('User-Agent', '')
269
         user_agent = gen_useragent()
265
         user_agent = gen_useragent()
270
 
266
 
267
+        search_query = self.search_query
268
+
271
         # start search-reqest for all selected engines
269
         # start search-reqest for all selected engines
272
-        for selected_engine in self.engines:
270
+        for selected_engine in search_query.engines:
273
             if selected_engine['name'] not in engines:
271
             if selected_engine['name'] not in engines:
274
                 continue
272
                 continue
275
 
273
 
276
             engine = engines[selected_engine['name']]
274
             engine = engines[selected_engine['name']]
277
 
275
 
276
+            # skip suspended engines
277
+            if engine.suspend_end_time and engine.suspend_end_time <= time():
278
+                continue
279
+
278
             # if paging is not supported, skip
280
             # if paging is not supported, skip
279
-            if self.pageno > 1 and not engine.paging:
281
+            if search_query.pageno > 1 and not engine.paging:
280
                 continue
282
                 continue
281
 
283
 
282
             # if search-language is set and engine does not
284
             # if search-language is set and engine does not
283
             # provide language-support, skip
285
             # provide language-support, skip
284
-            if self.lang != 'all' and not engine.language_support:
286
+            if search_query.lang != 'all' and not engine.language_support:
285
                 continue
287
                 continue
286
 
288
 
287
-            if self.time_range and not engine.time_range_support:
289
+            # if time_range is not supported, skip
290
+            if search_query.time_range and not engine.time_range_support:
288
                 continue
291
                 continue
289
 
292
 
290
             # set default request parameters
293
             # set default request parameters
292
             request_params['headers']['User-Agent'] = user_agent
295
             request_params['headers']['User-Agent'] = user_agent
293
             request_params['category'] = selected_engine['category']
296
             request_params['category'] = selected_engine['category']
294
             request_params['started'] = time()
297
             request_params['started'] = time()
295
-            request_params['pageno'] = self.pageno
298
+            request_params['pageno'] = search_query.pageno
296
 
299
 
297
             if hasattr(engine, 'language') and engine.language:
300
             if hasattr(engine, 'language') and engine.language:
298
                 request_params['language'] = engine.language
301
                 request_params['language'] = engine.language
299
             else:
302
             else:
300
-                request_params['language'] = self.lang
303
+                request_params['language'] = search_query.lang
301
 
304
 
302
             # 0 = None, 1 = Moderate, 2 = Strict
305
             # 0 = None, 1 = Moderate, 2 = Strict
303
-            request_params['safesearch'] = request.preferences.get_value('safesearch')
304
-            request_params['time_range'] = self.time_range
305
-            request_params['advanced_search'] = self.is_advanced
306
+            request_params['safesearch'] = search_query.safesearch
307
+            request_params['time_range'] = search_query.time_range
306
 
308
 
307
             # update request parameters dependent on
309
             # update request parameters dependent on
308
             # search-engine (contained in engines folder)
310
             # search-engine (contained in engines folder)
309
-            engine.request(self.query.encode('utf-8'), request_params)
311
+            engine.request(search_query.query.encode('utf-8'), request_params)
310
 
312
 
311
             if request_params['url'] is None:
313
             if request_params['url'] is None:
312
                 # TODO add support of offline engines
314
                 # TODO add support of offline engines
346
                              selected_engine['name']))
348
                              selected_engine['name']))
347
 
349
 
348
         if not requests:
350
         if not requests:
349
-            return self
351
+            return self.result_container
350
         # send all search-request
352
         # send all search-request
351
         threaded_requests(requests)
353
         threaded_requests(requests)
352
         start_new_thread(gc.collect, tuple())
354
         start_new_thread(gc.collect, tuple())
353
 
355
 
354
         # return results, suggestions, answers and infoboxes
356
         # return results, suggestions, answers and infoboxes
355
-        return self
357
+        return self.result_container
358
+
359
+
360
+def search_with_plugins(do_search, search_query, request, request_data, result_container):
361
+    """Search using the do_search function and with plugins filtering.
362
+    Standalone function to have a well define locals().
363
+    result_container contains the results after the function call.
364
+    """
365
+    search = search_query
366
+
367
+    if plugins.call('pre_search', request, locals()):
368
+        do_search()
369
+
370
+    plugins.call('post_search', request, locals())
371
+
372
+    results = result_container.get_ordered_results()
373
+
374
+    for result in results:
375
+        plugins.call('on_result', request, locals())
376
+
377
+
378
+class SearchWithPlugins(Search):
379
+
380
+    def __init__(self, search_query, request):
381
+        super(SearchWithPlugins, self).__init__(search_query)
382
+        self.request = request
383
+        self.request_data = request.request_data
384
+
385
+    def search(self):
386
+
387
+        def do_search():
388
+            super(SearchWithPlugins, self).search()
389
+
390
+        search_with_plugins(do_search, self.search_query, self.request, self.request_data, self.result_container)
391
+        return self.result_container

+ 57
- 49
searx/webapp.py Ver fichero

62
 )
62
 )
63
 from searx.version import VERSION_STRING
63
 from searx.version import VERSION_STRING
64
 from searx.languages import language_codes
64
 from searx.languages import language_codes
65
-from searx.search import Search
66
-from searx.query import Query
65
+from searx.search import Search, SearchWithPlugins, get_search_query_from_webapp
66
+from searx.query import RawTextQuery, SearchQuery
67
 from searx.autocomplete import searx_bang, backends as autocomplete_backends
67
 from searx.autocomplete import searx_bang, backends as autocomplete_backends
68
 from searx.plugins import plugins
68
 from searx.plugins import plugins
69
 from searx.preferences import Preferences, ValidationException
69
 from searx.preferences import Preferences, ValidationException
364
 
364
 
365
 @app.before_request
365
 @app.before_request
366
 def pre_request():
366
 def pre_request():
367
+    # request.request_data
368
+    if request.method == 'POST':
369
+        request_data = request.form
370
+    elif request.method == 'GET':
371
+        request_data = request.args
372
+    else:
373
+        request_data = {}
374
+
375
+    request.request_data = request_data
376
+
367
     # merge GET, POST vars
377
     # merge GET, POST vars
368
     preferences = Preferences(themes, categories.keys(), engines, plugins)
378
     preferences = Preferences(themes, categories.keys(), engines, plugins)
369
     try:
379
     try:
373
         logger.warning('Invalid config')
383
         logger.warning('Invalid config')
374
     request.preferences = preferences
384
     request.preferences = preferences
375
 
385
 
386
+    # request.form
376
     request.form = dict(request.form.items())
387
     request.form = dict(request.form.items())
377
     for k, v in request.args.items():
388
     for k, v in request.args.items():
378
         if k not in request.form:
389
         if k not in request.form:
379
             request.form[k] = v
390
             request.form[k] = v
380
 
391
 
392
+    # request.user_plugins
381
     request.user_plugins = []
393
     request.user_plugins = []
382
     allowed_plugins = preferences.plugins.get_enabled()
394
     allowed_plugins = preferences.plugins.get_enabled()
383
     disabled_plugins = preferences.plugins.get_disabled()
395
     disabled_plugins = preferences.plugins.get_disabled()
400
             'index.html',
412
             'index.html',
401
         )
413
         )
402
 
414
 
415
+    # search
416
+    search_query = None
417
+    result_container = None
403
     try:
418
     try:
404
-        search = Search(request)
419
+        search_query = get_search_query_from_webapp(request.preferences, request.request_data)
420
+        # search = Search(search_query) #  without plugins
421
+        search = SearchWithPlugins(search_query, request)
422
+        result_container = search.search()
405
     except:
423
     except:
406
         return render(
424
         return render(
407
             'index.html',
425
             'index.html',
408
         )
426
         )
409
 
427
 
410
-    if plugins.call('pre_search', request, locals()):
411
-        search.search(request)
412
-
413
-    plugins.call('post_search', request, locals())
428
+    results = result_container.get_ordered_results()
414
 
429
 
415
-    results = search.result_container.get_ordered_results()
430
+    # UI
431
+    advanced_search = request.request_data.get('advanced_search', None)
432
+    output_format = request.request_data.get('format', 'html')
433
+    if output_format not in ['html', 'csv', 'json', 'rss']:
434
+        output_format = 'html'
416
 
435
 
436
+    # output
417
     for result in results:
437
     for result in results:
418
-
419
-        plugins.call('on_result', request, locals())
420
-        if not search.paging and engines[result['engine']].paging:
421
-            search.paging = True
422
-
423
-        if search.request_data.get('format', 'html') == 'html':
438
+        if output_format == 'html':
424
             if 'content' in result and result['content']:
439
             if 'content' in result and result['content']:
425
-                result['content'] = highlight_content(result['content'][:1024], search.query.encode('utf-8'))
426
-            result['title'] = highlight_content(result['title'], search.query.encode('utf-8'))
440
+                result['content'] = highlight_content(result['content'][:1024], search_query.query.encode('utf-8'))
441
+            result['title'] = highlight_content(result['title'], search_query.query.encode('utf-8'))
427
         else:
442
         else:
428
             if result.get('content'):
443
             if result.get('content'):
429
                 result['content'] = html_to_text(result['content']).strip()
444
                 result['content'] = html_to_text(result['content']).strip()
450
                 else:
465
                 else:
451
                     result['publishedDate'] = format_date(result['publishedDate'])
466
                     result['publishedDate'] = format_date(result['publishedDate'])
452
 
467
 
453
-    number_of_results = search.result_container.results_number()
454
-    if number_of_results < search.result_container.results_length():
468
+    number_of_results = result_container.results_number()
469
+    if number_of_results < result_container.results_length():
455
         number_of_results = 0
470
         number_of_results = 0
456
 
471
 
457
-    if search.request_data.get('format') == 'json':
458
-        return Response(json.dumps({'query': search.query,
472
+    if output_format == 'json':
473
+        return Response(json.dumps({'query': search_query.query,
459
                                     'number_of_results': number_of_results,
474
                                     'number_of_results': number_of_results,
460
                                     'results': results}),
475
                                     'results': results}),
461
                         mimetype='application/json')
476
                         mimetype='application/json')
462
-    elif search.request_data.get('format') == 'csv':
477
+    elif output_format == 'csv':
463
         csv = UnicodeWriter(cStringIO.StringIO())
478
         csv = UnicodeWriter(cStringIO.StringIO())
464
         keys = ('title', 'url', 'content', 'host', 'engine', 'score')
479
         keys = ('title', 'url', 'content', 'host', 'engine', 'score')
465
         csv.writerow(keys)
480
         csv.writerow(keys)
468
             csv.writerow([row.get(key, '') for key in keys])
483
             csv.writerow([row.get(key, '') for key in keys])
469
         csv.stream.seek(0)
484
         csv.stream.seek(0)
470
         response = Response(csv.stream.read(), mimetype='application/csv')
485
         response = Response(csv.stream.read(), mimetype='application/csv')
471
-        cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search.query.encode('utf-8'))
486
+        cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query.encode('utf-8'))
472
         response.headers.add('Content-Disposition', cont_disp)
487
         response.headers.add('Content-Disposition', cont_disp)
473
         return response
488
         return response
474
-    elif search.request_data.get('format') == 'rss':
489
+    elif output_format == 'rss':
475
         response_rss = render(
490
         response_rss = render(
476
             'opensearch_response_rss.xml',
491
             'opensearch_response_rss.xml',
477
             results=results,
492
             results=results,
478
-            q=search.request_data['q'],
493
+            q=request.request_data['q'],
479
             number_of_results=number_of_results,
494
             number_of_results=number_of_results,
480
             base_url=get_base_url()
495
             base_url=get_base_url()
481
         )
496
         )
484
     return render(
499
     return render(
485
         'results.html',
500
         'results.html',
486
         results=results,
501
         results=results,
487
-        q=search.request_data['q'],
488
-        selected_categories=search.categories,
489
-        paging=search.paging,
502
+        q=request.request_data['q'],
503
+        selected_categories=search_query.categories,
504
+        pageno=search_query.pageno,
505
+        time_range=search_query.time_range,
490
         number_of_results=format_decimal(number_of_results),
506
         number_of_results=format_decimal(number_of_results),
491
-        pageno=search.pageno,
492
-        advanced_search=search.is_advanced,
493
-        time_range=search.time_range,
507
+        advanced_search=advanced_search,
508
+        suggestions=result_container.suggestions,
509
+        answers=result_container.answers,
510
+        infoboxes=result_container.infoboxes,
511
+        paging=result_container.paging,
494
         base_url=get_base_url(),
512
         base_url=get_base_url(),
495
-        suggestions=search.result_container.suggestions,
496
-        answers=search.result_container.answers,
497
-        infoboxes=search.result_container.infoboxes,
498
         theme=get_current_theme_name(),
513
         theme=get_current_theme_name(),
499
         favicons=global_favicons[themes.index(get_current_theme_name())]
514
         favicons=global_favicons[themes.index(get_current_theme_name())]
500
     )
515
     )
511
 @app.route('/autocompleter', methods=['GET', 'POST'])
526
 @app.route('/autocompleter', methods=['GET', 'POST'])
512
 def autocompleter():
527
 def autocompleter():
513
     """Return autocompleter results"""
528
     """Return autocompleter results"""
514
-    request_data = {}
515
-
516
-    # select request method
517
-    if request.method == 'POST':
518
-        request_data = request.form
519
-    else:
520
-        request_data = request.args
521
 
529
 
522
     # set blocked engines
530
     # set blocked engines
523
     disabled_engines = request.preferences.engines.get_disabled()
531
     disabled_engines = request.preferences.engines.get_disabled()
524
 
532
 
525
     # parse query
533
     # parse query
526
-    query = Query(request_data.get('q', '').encode('utf-8'), disabled_engines)
527
-    query.parse_query()
534
+    raw_text_query = RawTextQuery(request.request_data.get('q', '').encode('utf-8'), disabled_engines)
535
+    raw_text_query.parse_query()
528
 
536
 
529
     # check if search query is set
537
     # check if search query is set
530
-    if not query.getSearchQuery():
538
+    if not raw_text_query.getSearchQuery():
531
         return '', 400
539
         return '', 400
532
 
540
 
533
     # run autocompleter
541
     # run autocompleter
534
     completer = autocomplete_backends.get(request.preferences.get_value('autocomplete'))
542
     completer = autocomplete_backends.get(request.preferences.get_value('autocomplete'))
535
 
543
 
536
     # parse searx specific autocompleter results like !bang
544
     # parse searx specific autocompleter results like !bang
537
-    raw_results = searx_bang(query)
545
+    raw_results = searx_bang(raw_text_query)
538
 
546
 
539
     # normal autocompletion results only appear if max 3 inner results returned
547
     # normal autocompletion results only appear if max 3 inner results returned
540
     if len(raw_results) <= 3 and completer:
548
     if len(raw_results) <= 3 and completer:
545
         else:
553
         else:
546
             language = language.split('_')[0]
554
             language = language.split('_')[0]
547
         # run autocompletion
555
         # run autocompletion
548
-        raw_results.extend(completer(query.getSearchQuery(), language))
556
+        raw_results.extend(completer(raw_text_query.getSearchQuery(), language))
549
 
557
 
550
     # parse results (write :language and !engine back to result string)
558
     # parse results (write :language and !engine back to result string)
551
     results = []
559
     results = []
552
     for result in raw_results:
560
     for result in raw_results:
553
-        query.changeSearchQuery(result)
561
+        raw_text_query.changeSearchQuery(result)
554
 
562
 
555
         # add parsed result
563
         # add parsed result
556
-        results.append(query.getFullQuery())
564
+        results.append(raw_text_query.getFullQuery())
557
 
565
 
558
     # return autocompleter results
566
     # return autocompleter results
559
-    if request_data.get('format') == 'x-suggestions':
560
-        return Response(json.dumps([query.query, results]),
567
+    if request.request_data.get('format') == 'x-suggestions':
568
+        return Response(json.dumps([raw_text_query.query, results]),
561
                         mimetype='application/json')
569
                         mimetype='application/json')
562
 
570
 
563
     return Response(json.dumps(results),
571
     return Response(json.dumps(results),

+ 10
- 11
tests/unit/test_plugins.py Ver fichero

6
 
6
 
7
 
7
 
8
 def get_search_mock(query, **kwargs):
8
 def get_search_mock(query, **kwargs):
9
-    return {'search': Mock(query=query,
10
-                           result_container=Mock(answers=set()),
11
-                           **kwargs)}
9
+    return {'search': Mock(query=query, **kwargs),
10
+            'result_container': Mock(answers=set())}
12
 
11
 
13
 
12
 
14
 class PluginStoreTest(SearxTestCase):
13
 class PluginStoreTest(SearxTestCase):
54
         request.headers.getlist.return_value = []
53
         request.headers.getlist.return_value = []
55
         ctx = get_search_mock(query='ip', pageno=1)
54
         ctx = get_search_mock(query='ip', pageno=1)
56
         store.call('post_search', request, ctx)
55
         store.call('post_search', request, ctx)
57
-        self.assertTrue('127.0.0.1' in ctx['search'].result_container.answers)
56
+        self.assertTrue('127.0.0.1' in ctx['result_container'].answers)
58
 
57
 
59
         ctx = get_search_mock(query='ip', pageno=2)
58
         ctx = get_search_mock(query='ip', pageno=2)
60
         store.call('post_search', request, ctx)
59
         store.call('post_search', request, ctx)
61
-        self.assertFalse('127.0.0.1' in ctx['search'].result_container.answers)
60
+        self.assertFalse('127.0.0.1' in ctx['result_container'].answers)
62
 
61
 
63
         # User agent test
62
         # User agent test
64
         request = Mock(user_plugins=store.plugins,
63
         request = Mock(user_plugins=store.plugins,
67
 
66
 
68
         ctx = get_search_mock(query='user-agent', pageno=1)
67
         ctx = get_search_mock(query='user-agent', pageno=1)
69
         store.call('post_search', request, ctx)
68
         store.call('post_search', request, ctx)
70
-        self.assertTrue('Mock' in ctx['search'].result_container.answers)
69
+        self.assertTrue('Mock' in ctx['result_container'].answers)
71
 
70
 
72
         ctx = get_search_mock(query='user-agent', pageno=2)
71
         ctx = get_search_mock(query='user-agent', pageno=2)
73
         store.call('post_search', request, ctx)
72
         store.call('post_search', request, ctx)
74
-        self.assertFalse('Mock' in ctx['search'].result_container.answers)
73
+        self.assertFalse('Mock' in ctx['result_container'].answers)
75
 
74
 
76
         ctx = get_search_mock(query='user-agent', pageno=1)
75
         ctx = get_search_mock(query='user-agent', pageno=1)
77
         store.call('post_search', request, ctx)
76
         store.call('post_search', request, ctx)
78
-        self.assertTrue('Mock' in ctx['search'].result_container.answers)
77
+        self.assertTrue('Mock' in ctx['result_container'].answers)
79
 
78
 
80
         ctx = get_search_mock(query='user-agent', pageno=2)
79
         ctx = get_search_mock(query='user-agent', pageno=2)
81
         store.call('post_search', request, ctx)
80
         store.call('post_search', request, ctx)
82
-        self.assertFalse('Mock' in ctx['search'].result_container.answers)
81
+        self.assertFalse('Mock' in ctx['result_container'].answers)
83
 
82
 
84
         ctx = get_search_mock(query='What is my User-Agent?', pageno=1)
83
         ctx = get_search_mock(query='What is my User-Agent?', pageno=1)
85
         store.call('post_search', request, ctx)
84
         store.call('post_search', request, ctx)
86
-        self.assertTrue('Mock' in ctx['search'].result_container.answers)
85
+        self.assertTrue('Mock' in ctx['result_container'].answers)
87
 
86
 
88
         ctx = get_search_mock(query='What is my User-Agent?', pageno=2)
87
         ctx = get_search_mock(query='What is my User-Agent?', pageno=2)
89
         store.call('post_search', request, ctx)
88
         store.call('post_search', request, ctx)
90
-        self.assertFalse('Mock' in ctx['search'].result_container.answers)
89
+        self.assertFalse('Mock' in ctx['result_container'].answers)