Безопасный eval в python

Об этом уже достаточно много сказано, но может и мой вариант кому-то приглянется. Обычно запрещают список опасных функций для eval’а. Можно пойти чуть по другому пути и наоборот, разрешить, что-то. Я набросал пример, цель которого была - вычисление математических выражений. Чтобы было не так скучно, сделал это в django через аякс :)

Итак, views.py

import re, json
from django.shortcuts import render_to_response
from django.http import HttpResponse

#...

def calc(request):
    return render_to_response('calc.html', {})

def calc_json_expression(request):
    p = re.compile(r'[-+*\/\(\)0-9(sin)(cos)(tan)]*', re.IGNORECASE)
    calcmath = request.POST["calc-math"]
    res = ''.join(p.findall(calcmath))

    from math import sin, cos, tan

    json_data = {}
    try:
        json_data['exp'] = res
        json_data['res'] = eval(res)
    except:
        json_data['exp'] = ''
        json_data['res'] = '0'
    return HttpResponse(json.dumps(json_data), mimetype="application/json")

urls.py

urlpatterns = patterns('kernel.views',
    # ...
    url(r'^calc/$', 'calc'),
    url(r'^calc-json-expression/$', 'calc_json_expression'),
)

и файл шаблона calc.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
<body>
    <h1>Calc</h1>
    <form method="post" id="calc-form">
        <p><label for="calc-math">Print math expression</label><input type="text" name="calc-math" id="calc-math" value="" placeholder="Expression"></p>
        <p>Expression: <span id="calc-math-expression"></span></p>
        <p>Result: <span id="calc-math-answer">0</span></p>
    </form>

    <script type="text/javascript">
        $('#calc-math').keyup(function() {
            $.post('calc-json-expression/', $("#calc-form").serialize(),function(data){
                $('#calc-math-answer').text(data["res"]);
                $('#calc-math-expression').text(data["exp"]);
            }, "json");
        });
    </script>
</body>
</html>

Самое интересное во всем этом коде - выбор допустимых символов и слов регулярным выражением. Сюда можно добавить и другие выражения, но принцип должен быть понятен.

p = re.compile(r'[-+*\/\(\)0-9(sin)(cos)(tan)]*', re.IGNORECASE)

Собственно, это и есть мой способ, подобных при поиске “безопасный eval” не увидел. Проблема, с которой столкнулся - это ограничение на время исполнения скрипта. Такой штуки нет ни в джанго, ни в питоне. Поэтому 10 в миллиардной степени, например, этот скрипт не обработает, а сайт просто упадет. Есть решения, интернет ими кишит, но они, в основном, только для *nix, либо практически нерабочие. Если кто-то найдет кроссплатформенный рабочий код - отпишитесь, пожалуйста :)

django javascript python