searxng/searx/plugins/calculator/calculator_process.py
Alexandre Flament 1a11282ab4 [fix] calculator: call python using subprocess.Popen
call Python directly with -S and -I parameters to skip loading
of the standard library and the SearXNG module.

The actual calculator is moved to a standalone script: calculator_process.py
2025-01-11 23:33:29 +00:00

58 lines
1.2 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Standalone script to actually calculate mathematical expressions using ast
This is not a module, the SearXNG modules are not available here
"""
import ast
import sys
import operator
from typing import Callable
operators: dict[type, Callable] = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.BitXor: operator.xor,
ast.USub: operator.neg,
}
def _eval_expr(expr):
"""
>>> _eval_expr('2^6')
4
>>> _eval_expr('2**6')
64
>>> _eval_expr('1 + 2*3**(4^5) / (6 + -7)')
-5.0
"""
try:
return _eval(ast.parse(expr, mode='eval').body)
except ZeroDivisionError:
# This is undefined
return ""
def _eval(node):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp):
return operators[type(node.op)](_eval(node.left), _eval(node.right))
if isinstance(node, ast.UnaryOp):
return operators[type(node.op)](_eval(node.operand))
raise TypeError(node)
def main():
print(_eval_expr(sys.argv[1]), end="")
if __name__ == "__main__":
main()