Преглед на файлове

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 преди 8 години
родител
ревизия
67e11c42b9
променени са 6 файла, в които са добавени 266 реда и са изтрити 196 реда
  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 Целия файл

@@ -37,10 +37,10 @@ def post_search(request, ctx):
37 37
             ip = x_forwarded_for[0]
38 38
         else:
39 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 42
     elif p.match(ctx['search'].query):
43 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 46
     return True

+ 18
- 2
searx/query.py Целия файл

@@ -25,8 +25,8 @@ import string
25 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 31
     def __init__(self, query, disabled_engines):
32 32
         self.query = query
@@ -130,3 +130,19 @@ class Query(object):
130 130
     def getFullQuery(self):
131 131
         # get full querry including whitespaces
132 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 Целия файл

@@ -128,6 +128,8 @@ class ResultContainer(object):
128 128
         self.suggestions = set()
129 129
         self.answers = set()
130 130
         self._number_of_results = []
131
+        self._ordered = False
132
+        self.paging = False
131 133
 
132 134
     def extend(self, engine_name, results):
133 135
         for result in list(results):
@@ -153,6 +155,9 @@ class ResultContainer(object):
153 155
 
154 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 161
         for i, result in enumerate(results):
157 162
             try:
158 163
                 result['url'] = result['url'].decode('utf-8')
@@ -219,7 +224,7 @@ class ResultContainer(object):
219 224
             with RLock():
220 225
                 self._merged_results.append(result)
221 226
 
222
-    def get_ordered_results(self):
227
+    def order_results(self):
223 228
         for result in self._merged_results:
224 229
             score = result_score(result)
225 230
             result['score'] = score
@@ -269,8 +274,14 @@ class ResultContainer(object):
269 274
                 # update categoryIndex
270 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 286
     def results_length(self):
276 287
         return len(self._merged_results)

+ 163
- 127
searx/search.py Целия файл

@@ -25,9 +25,10 @@ from searx.engines import (
25 25
     categories, engines
26 26
 )
27 27
 from searx.utils import gen_useragent
28
-from searx.query import Query
28
+from searx.query import RawTextQuery, SearchQuery
29 29
 from searx.results import ResultContainer
30 30
 from searx import logger
31
+from searx.plugins import plugins
31 32
 
32 33
 logger = logger.getChild('search')
33 34
 
@@ -127,135 +128,130 @@ def make_callback(engine_name, callback, params, result_container):
127 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 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 217
             # if no category is specified for this search,
232 218
             # using user-defined default-configuration which
233 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 222
                 for ccateg in cookie_categories:
237 223
                     if ccateg in categories:
238
-                        self.categories.append(ccateg)
224
+                        query_categories.append(ccateg)
239 225
 
240 226
             # if still no category is specified, using general
241 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 231
             # using all engines for that search, which are
246 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 253
     # do search-request
258
-    def search(self, request):
254
+    def search(self):
259 255
         global number_of_searches
260 256
 
261 257
         # init vars
@@ -268,23 +264,30 @@ class Search(object):
268 264
         # user_agent = request.headers.get('User-Agent', '')
269 265
         user_agent = gen_useragent()
270 266
 
267
+        search_query = self.search_query
268
+
271 269
         # start search-reqest for all selected engines
272
-        for selected_engine in self.engines:
270
+        for selected_engine in search_query.engines:
273 271
             if selected_engine['name'] not in engines:
274 272
                 continue
275 273
 
276 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 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 282
                 continue
281 283
 
282 284
             # if search-language is set and engine does not
283 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 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 291
                 continue
289 292
 
290 293
             # set default request parameters
@@ -292,21 +295,20 @@ class Search(object):
292 295
             request_params['headers']['User-Agent'] = user_agent
293 296
             request_params['category'] = selected_engine['category']
294 297
             request_params['started'] = time()
295
-            request_params['pageno'] = self.pageno
298
+            request_params['pageno'] = search_query.pageno
296 299
 
297 300
             if hasattr(engine, 'language') and engine.language:
298 301
                 request_params['language'] = engine.language
299 302
             else:
300
-                request_params['language'] = self.lang
303
+                request_params['language'] = search_query.lang
301 304
 
302 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 309
             # update request parameters dependent on
308 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 313
             if request_params['url'] is None:
312 314
                 # TODO add support of offline engines
@@ -346,10 +348,44 @@ class Search(object):
346 348
                              selected_engine['name']))
347 349
 
348 350
         if not requests:
349
-            return self
351
+            return self.result_container
350 352
         # send all search-request
351 353
         threaded_requests(requests)
352 354
         start_new_thread(gc.collect, tuple())
353 355
 
354 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 Целия файл

@@ -62,8 +62,8 @@ from searx.utils import (
62 62
 )
63 63
 from searx.version import VERSION_STRING
64 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 67
 from searx.autocomplete import searx_bang, backends as autocomplete_backends
68 68
 from searx.plugins import plugins
69 69
 from searx.preferences import Preferences, ValidationException
@@ -364,6 +364,16 @@ def render(template_name, override_theme=None, **kwargs):
364 364
 
365 365
 @app.before_request
366 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 377
     # merge GET, POST vars
368 378
     preferences = Preferences(themes, categories.keys(), engines, plugins)
369 379
     try:
@@ -373,11 +383,13 @@ def pre_request():
373 383
         logger.warning('Invalid config')
374 384
     request.preferences = preferences
375 385
 
386
+    # request.form
376 387
     request.form = dict(request.form.items())
377 388
     for k, v in request.args.items():
378 389
         if k not in request.form:
379 390
             request.form[k] = v
380 391
 
392
+    # request.user_plugins
381 393
     request.user_plugins = []
382 394
     allowed_plugins = preferences.plugins.get_enabled()
383 395
     disabled_plugins = preferences.plugins.get_disabled()
@@ -400,30 +412,33 @@ def index():
400 412
             'index.html',
401 413
         )
402 414
 
415
+    # search
416
+    search_query = None
417
+    result_container = None
403 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 423
     except:
406 424
         return render(
407 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 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 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 442
         else:
428 443
             if result.get('content'):
429 444
                 result['content'] = html_to_text(result['content']).strip()
@@ -450,16 +465,16 @@ def index():
450 465
                 else:
451 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 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 474
                                     'number_of_results': number_of_results,
460 475
                                     'results': results}),
461 476
                         mimetype='application/json')
462
-    elif search.request_data.get('format') == 'csv':
477
+    elif output_format == 'csv':
463 478
         csv = UnicodeWriter(cStringIO.StringIO())
464 479
         keys = ('title', 'url', 'content', 'host', 'engine', 'score')
465 480
         csv.writerow(keys)
@@ -468,14 +483,14 @@ def index():
468 483
             csv.writerow([row.get(key, '') for key in keys])
469 484
         csv.stream.seek(0)
470 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 487
         response.headers.add('Content-Disposition', cont_disp)
473 488
         return response
474
-    elif search.request_data.get('format') == 'rss':
489
+    elif output_format == 'rss':
475 490
         response_rss = render(
476 491
             'opensearch_response_rss.xml',
477 492
             results=results,
478
-            q=search.request_data['q'],
493
+            q=request.request_data['q'],
479 494
             number_of_results=number_of_results,
480 495
             base_url=get_base_url()
481 496
         )
@@ -484,17 +499,17 @@ def index():
484 499
     return render(
485 500
         'results.html',
486 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 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 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 513
         theme=get_current_theme_name(),
499 514
         favicons=global_favicons[themes.index(get_current_theme_name())]
500 515
     )
@@ -511,30 +526,23 @@ def about():
511 526
 @app.route('/autocompleter', methods=['GET', 'POST'])
512 527
 def autocompleter():
513 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 530
     # set blocked engines
523 531
     disabled_engines = request.preferences.engines.get_disabled()
524 532
 
525 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 537
     # check if search query is set
530
-    if not query.getSearchQuery():
538
+    if not raw_text_query.getSearchQuery():
531 539
         return '', 400
532 540
 
533 541
     # run autocompleter
534 542
     completer = autocomplete_backends.get(request.preferences.get_value('autocomplete'))
535 543
 
536 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 547
     # normal autocompletion results only appear if max 3 inner results returned
540 548
     if len(raw_results) <= 3 and completer:
@@ -545,19 +553,19 @@ def autocompleter():
545 553
         else:
546 554
             language = language.split('_')[0]
547 555
         # run autocompletion
548
-        raw_results.extend(completer(query.getSearchQuery(), language))
556
+        raw_results.extend(completer(raw_text_query.getSearchQuery(), language))
549 557
 
550 558
     # parse results (write :language and !engine back to result string)
551 559
     results = []
552 560
     for result in raw_results:
553
-        query.changeSearchQuery(result)
561
+        raw_text_query.changeSearchQuery(result)
554 562
 
555 563
         # add parsed result
556
-        results.append(query.getFullQuery())
564
+        results.append(raw_text_query.getFullQuery())
557 565
 
558 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 569
                         mimetype='application/json')
562 570
 
563 571
     return Response(json.dumps(results),

+ 10
- 11
tests/unit/test_plugins.py Целия файл

@@ -6,9 +6,8 @@ from mock import Mock
6 6
 
7 7
 
8 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 13
 class PluginStoreTest(SearxTestCase):
@@ -54,11 +53,11 @@ class SelfIPTest(SearxTestCase):
54 53
         request.headers.getlist.return_value = []
55 54
         ctx = get_search_mock(query='ip', pageno=1)
56 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 58
         ctx = get_search_mock(query='ip', pageno=2)
60 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 62
         # User agent test
64 63
         request = Mock(user_plugins=store.plugins,
@@ -67,24 +66,24 @@ class SelfIPTest(SearxTestCase):
67 66
 
68 67
         ctx = get_search_mock(query='user-agent', pageno=1)
69 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 71
         ctx = get_search_mock(query='user-agent', pageno=2)
73 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 75
         ctx = get_search_mock(query='user-agent', pageno=1)
77 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 79
         ctx = get_search_mock(query='user-agent', pageno=2)
81 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 83
         ctx = get_search_mock(query='What is my User-Agent?', pageno=1)
85 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 87
         ctx = get_search_mock(query='What is my User-Agent?', pageno=2)
89 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)