Commit 6f00ff30 authored by Nivesh Krishna's avatar Nivesh Krishna
Browse files

Merge branch 'fix-calculator' into 'master'

Fix calculator

See merge request !122
parents 4cc35605 5f0de8f9
......@@ -20,3 +20,4 @@ linuxdoc==20211220
aiounittest==1.4.1
numexpr==2.8.1
werkzeug==2.0.3
wrapt-timeout-decorator==1.3.8
......@@ -18,3 +18,4 @@ redis==3.4.1
ring==0.7.3
numexpr==2.8.1
werkzeug==2.0.3
wrapt-timeout-decorator==1.3.8
\ No newline at end of file
from searx import logger
from numexpr import evaluate
from flask_babel import gettext
from wrapt_timeout_decorator import timeout
name = gettext('Calculator')
description = gettext('This plugin extends results when the query is a mathematical expression')
......@@ -13,12 +13,10 @@ def check_if_loaded():
logger.debug("initializing calculator plugin")
def is_really_big(query):
# For cases like 2**99999**9999
if len(query.split("**")) >= 3:
return True
# Add more cases if needed
return False
# Set timeout so that the plugin doesn't hang for long computations
@timeout(5)
def calculate(query):
return evaluate(query).item()
def post_search(request, search):
......@@ -27,8 +25,11 @@ def post_search(request, search):
try:
query = search.search_query.query.lower()
unmodified_query = query
# Replace all frequently used substitutes
query = query.replace("x", "*")
query = query.replace("^", "**")
query = query.replace("%", "*0.01")
# Not going to compute if only one number is present
try:
......@@ -45,18 +46,18 @@ def post_search(request, search):
if len(query) > 30:
return
# Not going to compute the result if the query is not within permissible range
if is_really_big(query):
raise OverflowError
# Multiply by float to upcast all numbers to floats
# https://numexpr.readthedocs.io/projects/NumExpr3/en/latest/user_guide.html#casting-rules
query += "*1.0"
value = evaluate(query).item()
value = calculate(query)
if type(value) in (int, float):
search.result_container.answers.clear()
answer = "{} = {}".format(unmodified_query, value)
search.result_container.answers[answer] = {'answer': answer, 'calculator': True}
except (ZeroDivisionError, ValueError, FloatingPointError, MemoryError, OverflowError) as e:
search.result_container.answers['calculator'] = {'answer': answer, 'calculator': True}
except (ZeroDivisionError, ValueError, FloatingPointError, MemoryError, OverflowError, TimeoutError) as e:
answer = gettext('Error')
search.result_container.answers[answer] = {'answer': answer, 'calculator': True}
search.result_container.answers['calculator'] = {'answer': answer, 'calculator': True}
except Exception as e:
logger.debug(e)
......
......@@ -137,3 +137,51 @@ class HashPluginTest(SearxTestCase):
self.assertTrue('sha512 hash digest: ee26b0dd4af7e749aa1a8ee3c10ae9923f6'
'18980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5'
'fa9ad8e6f57f50028a8ff' in search.result_container.answers['hash']['answer'])
class CalculatorPluginTest(SearxTestCase):
def test_PluginStore_init(self):
store = plugins.PluginStore()
store.register(plugins.calculator)
self.assertTrue(len(store.plugins) == 1)
request = Mock(remote_addr='127.0.0.1')
request.headers.getlist.return_value = []
# True addition test
search = get_search_mock(query='2+2', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('4'
in search.result_container.answers['calculator']['answer'])
# False addition test
search = get_search_mock(query='2+2', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('4' not
in search.result_container.answers['calculator']['answer'])
# no result test
search = get_search_mock(query='2+2', pageno=2)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('calculator'
in search.result_container.answers)
# no result test
search = get_search_mock(query='2+2/sdf', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('calculator'
in search.result_container.answers)
# error result test
search = get_search_mock(query='2+2/0', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('Error'
in search.result_container.answers['calculator']['answer'])
# error result test
search = get_search_mock(query='2**999999999**99999999', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('Error'
in search.result_container.answers['calculator']['answer'])
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment