在Python中编译和执行简单的用户定义代码
我想让我的用户能够为一个项目运行非常简单的Python函数。当然,我会想到在Python中编译和执行简单的用户定义代码,python,Python,我想让我的用户能够为一个项目运行非常简单的Python函数。当然,我会想到eval(),但这是一个巨大的风险。经过一段时间的思考,我意识到用户可能需要的大多数函数都是非常基本的,类似于最常见的excel函数。因此,我想维护一个字典,其中键是函数名,用户只能选择(由我)在该字典中定义的函数。例如: def add(a, b): return a + b def sum(numbers): result = 0 for number in numbers:
eval()
,但这是一个巨大的风险。经过一段时间的思考,我意识到用户可能需要的大多数函数都是非常基本的,类似于最常见的excel函数。因此,我想维护一个字典,其中键是函数名,用户只能选择(由我)在该字典中定义的函数。例如:
def add(a, b):
return a + b
def sum(numbers):
result = 0
for number in numbers:
result += number
return number
...
function_map = {
'add': add,
'sum': sum,
...
}
现在,如果用户将一行定义为
add(4,5)
,那么结果将是预期的9,但是,如果他们定义类似foo(4)
,因为键在我的字典中不存在,那么会出现错误。我的问题是:这有多安全?是否存在我在这里忽略的任何潜在漏洞?您可以使用适当的全局变量和局部变量来对进行评估。例如,这是我在中使用的wat
但你可能也会事先筛选表达式。拒绝包含import
或eval
或exec
的内容:
if any(j in expr for j in ('import', 'exec', 'eval')):
raise ValueError('import, exec and eval are not allowed')
上面链接的模块还包含使用ast
将Python计算转换为LaTeX数学表达式。您还可以使用ast
构建自定义表达式计算器
另外,这里是我制作的一个小堆栈
一个不同之处是,我向\u ops
值添加了每个运算符所需的参数数量,以便知道从堆栈中获取多少个操作数
import operator
import math
# Global constants {{{1
_add, _sub, _mul = operator.add, operator.sub, operator.mul
_truediv, _pow, _sqrt = operator.truediv, operator.pow, math.sqrt
_sin, _cos, _tan, _radians = math.sin, math.cos, math.tan, math.radians
_asin, _acos, _atan = math.asin, math.acos, math.atan
_degrees, _log, _log10 = math.degrees, math.log, math.log10
_e, _pi = math.e, math.pi
_ops = {
'+': (2, _add),
'-': (2, _sub),
'*': (2, _mul),
'/': (2, _truediv),
'**': (2, _pow),
'sin': (1, _sin),
'cos': (1, _cos),
'tan': (1, _tan),
'asin': (1, _asin),
'acos': (1, _acos),
'atan': (1, _atan),
'sqrt': (1, _sqrt),
'rad': (1, _radians),
'deg': (1, _degrees),
'ln': (1, _log),
'log': (1, _log10)
}
_okeys = tuple(_ops.keys())
_consts = {'e': _e, 'pi': _pi}
_ckeys = tuple(_consts.keys())
def postfix(expression): # {{{1
"""
Evaluate a postfix expression.
Arguments:
expression: The expression to evaluate. Should be a string or a
sequence of strings. In a string numbers and operators
should be separated by whitespace
Returns:
The result of the expression.
"""
if isinstance(expression, str):
expression = expression.split()
stack = []
for val in expression:
if val in _okeys:
n, op = _ops[val]
if n > len(stack):
raise ValueError('not enough data on the stack')
args = stack[-n:]
stack[-n:] = [op(*args)]
elif val in _ckeys:
stack.append(_consts[val])
else:
stack.append(float(val))
return stack[-1]
想想也许是最近的岔口
import operator
import math
# Global constants {{{1
_add, _sub, _mul = operator.add, operator.sub, operator.mul
_truediv, _pow, _sqrt = operator.truediv, operator.pow, math.sqrt
_sin, _cos, _tan, _radians = math.sin, math.cos, math.tan, math.radians
_asin, _acos, _atan = math.asin, math.acos, math.atan
_degrees, _log, _log10 = math.degrees, math.log, math.log10
_e, _pi = math.e, math.pi
_ops = {
'+': (2, _add),
'-': (2, _sub),
'*': (2, _mul),
'/': (2, _truediv),
'**': (2, _pow),
'sin': (1, _sin),
'cos': (1, _cos),
'tan': (1, _tan),
'asin': (1, _asin),
'acos': (1, _acos),
'atan': (1, _atan),
'sqrt': (1, _sqrt),
'rad': (1, _radians),
'deg': (1, _degrees),
'ln': (1, _log),
'log': (1, _log10)
}
_okeys = tuple(_ops.keys())
_consts = {'e': _e, 'pi': _pi}
_ckeys = tuple(_consts.keys())
def postfix(expression): # {{{1
"""
Evaluate a postfix expression.
Arguments:
expression: The expression to evaluate. Should be a string or a
sequence of strings. In a string numbers and operators
should be separated by whitespace
Returns:
The result of the expression.
"""
if isinstance(expression, str):
expression = expression.split()
stack = []
for val in expression:
if val in _okeys:
n, op = _ops[val]
if n > len(stack):
raise ValueError('not enough data on the stack')
args = stack[-n:]
stack[-n:] = [op(*args)]
elif val in _ckeys:
stack.append(_consts[val])
else:
stack.append(float(val))
return stack[-1]