Позволяет выполнить строку-выражение с кодом на Python
Функция eval() выполняет строку-выражение, переданную ей в качестве обязательного аргумента и возвращает результат выполнения этой строки.
Аргументами eval() являются строка-выражение expression , которую требуется исполнить и необязательные глобальные globals и локальные locals значения. Передаваемые в функцию глобальные переменные должны быть словарем dict . Передаваемые локальные переменные могут быть любым отображающим объектом.
Если глобальные переменные указаны, но словарь globals не содержит атрибута __builtins__ данные переданного словаря со значениями будут дополнены данными общего глобального пространства, перед разбором выражения. Таким образом, выражение будет иметь доступ ко всем встроенным модулям.
Если локальные locals переменные не указаны, то используется словарь глобального пространства.
Если оба словаря опущены, выражение выполняется с глобальными и локальными значениями в среде, где функция eval() вызывается. Обратите внимание, что eval() не имеет доступа к вложенным областям
Функция eval() также может быть использована для выполнения кода, который возвращает функция compile() . Если объект кода в compile() собран в режиме exec будет возвращено None .
Функции eval() можно передавать результаты функций globals() и locals() .
Если вам необходимо выполнить динамический код, записанный в строку, то обратитесь к документации по функции exec() .
Примеры выполнения строки-выражения с кодом функцией eval() .
>>> x = "print('Привет')" >>> eval(x) # Привет >>> y = 'print("5 + 10 =", (5+10))' >>> eval(y) # 5 + 10 = 15 >>> s=3 >>> eval('s==3') # True >>> eval('s + 1') # 4 >>> eval('s') # 3 >>> eval('str(s)+"test"') # '3test'
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Функция abs(), абсолютное значение числа
- Функция all(), все элементы True
- Функция any(), хотя бы один элемент True
- Функция ascii(), преобразует строку в ASCII
- Функция bin(), число в двоичную строку
- Класс bool(), логическое значение объекта
- Функция breakpoint(), отладчик кода
- Класс bytearray(), преобразует в массив байтов
- Класс bytes(), преобразует в строку байтов
- Функция callable(), проверяет можно ли вызвать объект
- Функция chr(), число в символ Юникода
- Класс classmethod, делает функцию методом класса
- Функция compile() компилирует блок кода Python
- Класс complex(), преобразует в комплексное число
- Функция delattr(), удаляет атрибут объекта
- Класс dict() создает словарь
- Функция dir(), все атрибуты объекта
- Функция divmod(), делит числа с остатком
- Функция enumerate(), счетчик элементов последовательности
- Функция eval(), выполняет строку-выражение с кодом
- Функция exec(), выполняет блок кода
- Функция filter(), фильтрует список по условию
- Класс float(), преобразует в вещественное число
- Функция format(), форматирует значение переменной
- Класс frozenset(), преобразует в неизменяемое множество
- Функция getattr(), значение атрибута по имени
- Функция globals(), переменные глобальной области
- Функция hasattr(), наличие атрибута объекта
- Функция hash(), хэш-значение объекта
- Функция help(), справка по любому объекту
- Функция hex(), число в шестнадцатеричную строку
- Функция id(), идентификатор объекта
- Функция input(), ввод данных с клавиатуры
- Класс int(), преобразует в тип int
- Функция isinstance(), принадлежность экземпляра к классу
- Функция issubclass(), проверяет наследование класса
- Функция iter(), создает итератор
- Функция len(), количество элементов объекта
- Класс list(), преобразовывает в список
- Функция locals(), переменные локальной области
- Функция map(), обработка последовательности без цикла
- Функция max(), максимальное значение элемента
- Класс memoryview(), ссылка на буфер обмена
- Функция min(), минимальное значение элемента
- Функция next(), следующий элемент итератора
- Класс object(), возвращает безликий объект
- Функция oct(), число в восьмеричную строку
- Функция open(), открывает файл на чтение/запись
- Функция ord(), число символа Unicode
- Функция pow(), возводит число в степень
- Функция print(), печатает объект
- Класс property(), метод класса как свойство
- Класс range(), генерирует арифметические последовательности
- Функция repr(), описание объекта
- Функция reversed(), разворачивает последовательность
- Функция round(), округляет число
- Класс set(), создает или преобразовывает в множество
- Функция setattr(), создает атрибут объекта
- Класс slice(), шаблон среза
- Функция sorted(), выполняет сортировку
- Декоратор staticmethod(), метод класса в статический метод
- Класс str(), преобразует объект в строку
- Функция sum(), сумма последовательности
- Функция super(), доступ к унаследованным методам
- Класс tuple(), создает или преобразует в кортеж
- Класс type(), возвращает тип объекта
- Функция vars(), словарь переменных объекта
- Функция zip(), объединить элементы в список кортежей
- Функция __import__(), находит и импортирует модуль
- Функция aiter(), создает асинхронный итератор
- Функция anext(), следующий элемент асинхронного итератора
Функция Eval
Функция Eval используется для оценки выражение, которая приводит к в виде текстовой строки или числового значения.
Вы можете составить строку и передать ее функции Eval точно так же, как если бы эта строка была настоящим выражением. Функция Eval вычисляет строковое выражение и возвращает его значение. Например, Eval(«1 + 1») возвращает 2.
Если в функцию Eval передать строку, содержащую имя другой функции, функция Eval вернет значение, возвращаемое этой функцией. Например, Eval(«Chr$(65)») возвращает значение «A».
Eval ( строковое_выражение )
Аргумент строковое_выражение — это выражение, результатом вычисления которого является строка, состоящая из букв и цифр. В качестве строкового_выражения можно, например, указать функцию, которая возвращает строку или числовое значение. В других случаях это может быть ссылка на элемент управления формы. Аргумент строковое_выражение должен в результате вычисления давать строковое или числовое значение; его результатом не может быть объект объект Microsoft Access.
Примечание: В примерах ниже показано, как использовать эту функцию в модуле Visual Basic для приложений (VBA). Чтобы получить дополнительные сведения о работе с VBA, выберите Справочник разработчика в раскрывающемся списке рядом с полем Поиск и введите одно или несколько слов в поле поиска.
Примечание: Если вы указываете в аргументе строковое_выражение функции Eval имя другой функции, необходимо ставить после него в скобки. Например:
‘ ShowNames is user-defined function.
Debug.Print Eval(«ShowNames()»)
Debug.Print Eval(«StrComp(«»Joe»»,»»joe»», 1)»)
Замечания
Функцию Eval можно использовать в вычисляемый элемент управления в форме, отчете, макросах или модулях. Функция Eval возвращает variant, который является строкой или числом.
Аргументом строковое_выражение должно быть выражение, сохраненное в строке. Если функции Eval передать строку, которая содержит только текст, а не числовое выражение или имя функции, возникнет ошибка во время выполнения. Например, выполнение функции Eval(«Smith») приведет к ошибке.
С помощью функции Eval можно определить значение, хранящееся в свойстве Значение элемента управления. В следующем примере в функцию Eval передается строка, содержащая полную ссылку на элемент управления. В диалоговом окне будет отображаться текущее значение элемента управления.
Dim ctl As Control
Dim strCtl As String
Set ctl = Forms!Employees!LastName
strCtl = «Forms!Employees!LastName»
MsgBox («The current value of » & ctl.Name & _
» is » & Eval(strCtl))
Функция Eval дает возможность применять операторы выражений, недопустимые в модулях Visual Basic для приложений (VBA). Например, в коде программы нельзя использовать непосредственно операторы SQL Between. And или In, но их можно использовать в выражении, передаваемом в функцию Eval.
В следующем примере определяется, является ли значение элемента управления ShipRegion (Регион доставки) в форме Orders (Заказы) одним из заданных кодов регионов. Если поле содержит одно из таких обозначений, переменная intState примет значение True (-1). Обратите внимание, что для включения одной строки в другую нужно использовать одинарные кавычки (‘).
Dim intState As Integer
intState = Eval(«Forms!Orders!ShipRegion In » _
& «(‘AK’, ‘CA’, ‘ID’, ‘WA’, ‘MT’, ‘NM’, ‘OR’)»)
Примеры запросов
SELECT Eval(«15+3») AS Expr1 FROM ProductSales GROUP BY Eval(«15+3»);
Вычисляет строковую величину и результат. Результат: 18.
SELECT Eval(«Date()+month(date()))») AS NewDate FROM ProductSales Group by Eval(«Date()+month(date())»);
Оценивает строковую величину, определяет функции и выполняет их. Результат: текущая системная дата + текущий месяц, отображаемая в столбце NewDate.
Примеры VBA
В следующем примере предполагается, что имеется 50 функций, обозначенных A1, A2 и т. д. Функция Eval используется для последовательного вызова этих функций.
Sub CallSeries()
Dim intI As Integer
For intI = 1 To 50
Eval(«A» & intI & «()»)
Next intI
End Sub
В следующем примере событие «Щелкните» активируется так же, как если бы пользователь нажал кнопку в форме. Если значение свойства OnClick кнопки начинается со знака равной (=), то есть это имя функции, функция Eval вызывает функцию, что эквивалентно запуску события Click. Если значение не начинается со знака равно, ему необходимо назвать макрос. Метод RunMacro объекта DoCmd выполняет именуемый макрос.
Dim ctl As Control
Dim varTemp As Variant
Set ctl = Forms!Contacts!HelpButton
If (Left(ctl.OnClick, 1) = » ocArticleFooterShareContainer»>
И снова про опасность eval()
Сколько было сломано копий при обсуждении вопроса «Возможно ли сделать eval безопасным?» — невозможно сосчитать. Всегда находится кто-то, кто утверждает, что нашёл способ оградиться от всех возможных последствий выполнения этой функции.
Когда мне понадобилось найти развёрнутый ответ на этот вопрос, я наткнулся на один пост. Меня приятно удивила глубина исследования, так что я решил, что это стоит перевести.
Коротко о проблеме
В Python есть встроенная функция eval() , которая выполняет строку с кодом и возвращает результат выполнения:
assert eval("2 + 3 * len('hello')") == 17
Это очень мощная, но в то же время и очень опасная инструкция, особенно если строки, которые вы передаёте в eval , получены не из доверенного источника. Что будет, если строкой, которую мы решим скормить eval ‘у, окажется os.system(‘rm -rf /’) ? Интерпретатор честно запустит процесс удаления всех данных с компьютера, и хорошо ещё, если он будет выполняться от имени наименее привилегированного пользователя (в последующих примерах я буду использовать clear ( cls , если вы используете Windows) вместо rm -rf / , чтобы никто из читателей случайно не выстрелил себе в ногу).
Какие есть решения?
Некоторые утверждают, что возможно сделать eval безопасным, если запускать его без доступа к символам из globals. В качестве второго (опционального) аргумента eval() принимает словарь, который будет использован вместо глобального пространства имён (все классы, методы, переменные и пр., объявленные на «верхнем» уровне, доступные из любой точки кода) кодом, который будет выполнен eval ‘ом. Если eval вызывается без этого аргумента, он использует текущее глобальное пространство имён, в которое мог быть импортирован модуль os . Если же передать пустой словарь, глобальное пространство имён для eval ‘а будет пустым. Вот такой код уже не сможет выполниться и возбудит исключение NameError: name ‘os’ is not defined :
eval("os.system('clear')", <>)
Однако мы всё ещё можем импортировать модули и обращаться к ним, используя встроенную функцию __import__ . Так, код ниже отработает без ошибок:
eval("__import__('os').system('clear')", <>)
Следующей попыткой обычно становится решение запретить доступ к __builtins__ изнутри eval ‘a, так как имена, подобные __import__, доступны нам потому, что они находятся в глобальной переменной __builtins__ . Если мы явно передадим вместо неё пустой словарь, код ниже уже не сможет быть выполнен:
eval("__import__('os').system('clear')", >) # NameError: name '__import__' is not defined
Ну а теперь-то мы в безопасности?
Некоторые говорят, что «да» и совершают ошибку. Для примера, вот этот небольшой кусок кода вызовет segfault , если вы запустите его в CPython:
s = """ (lambda fc=( lambda n: [ c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n ][0] ): fc("function")( fc("code")( 0,0,0,0,"KABOOM",(),(),(),"","",0,"" ),<> )() )() """ eval(s, >)
Итак, давайте разберёмся, что же здесь происходит. Начнём с этого:
().__class__.__bases__[0]
Как многие могли догадаться, это просто один из способов обратиться к object . Мы не можем просто написать object , так как __builtins__ пусты, но мы можем создать пустой кортеж (тьюпл), первым базовым классом которого является object и, пройдясь по его свойствам, получить доступ к классу object .
Теперь мы получаем список всех классов, которые наследуют object или, иными словами, список всех классов, объявленных в программе на данный момент:
().__class__.__bases__[0].__subclasses__()
Если заменить для удобочитаемости это выражение на ALL_CLASSES , нетрудно будет заметить, что выражение ниже находит класс по его имени:
[c for c in ALL_CLASSES if c.__name__ == n][0]
Далее в коде нам надо будет дважды искать класс, так что создадим функцию:
lambda n: [c for c in ALL_CLASSES if c.__name__ == n][0]
Чтобы вызвать функцию, надо как-то её назвать, но, так как мы будем выполнять этот код внутри eval ‘a, мы не можем ни объявить функцию (используя def ), ни использовать оператор присвоения, чтобы привязать нашу лямбду к какой-нибудь переменной.
Однако, есть и третий вариант: параметры по умолчанию. При объявлении лямбды, как и при объявлении любой обычной функции, мы можем задать параметры по умолчанию, так что если мы поместим весь код внутри ещё одной лямбды, и зададим ей нашу, как параметр по умолчанию, — мы добьёмся желаемого:
(lambda fc=( lambda n: [ c for c in ALL_CLASSES if c.__name__ == n ][0] ): # теперь мы можем обращаться к нашей лямбде через fc )()
Итак, мы имеем функцию, которая умеет искать классы, и можем обращаться к ней по имени. Что дальше? Мы создадим объект класса code (внутренний класс, его экземпляром, например, является свойство func_code объекта функции):
fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,"")
Из всех инициализующих параметров нас интересует только «KABOOM». Это и есть последовательность байт-кодов, которую будет использовать наш объект, и, как вы уже могли догадаться, эта последовательность не является «хорошей». На самом деле любого байт-кода из неё хватило бы, так как всё это — бинарные операторы, которые будут вызваны при пустом стеке, что приведёт к segfault ‘у CPython. «KABOOM» просто выглядит забавнее, спасибо lvh за этот пример.
Итак, у нас есть объект класса code , но напрямую выполнить его мы не можем. Тогда создадим функцию, кодом которой и будет наш объект:
fc("function")(CODE_OBJECT, <>)
Ну и теперь, когда у нас есть функция, мы можем её выполнить. Конкретно эта функция попытается выполнить наш некорректно составленный байт-код и приведёт к краху интерпретатора.
Вот весь код ещё раз:
(lambda fc=(lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]): fc("function")(fc("code")(0,0,0,0,"KABOOM",(),(),(),"","",0,""),<>)() )()
Заключение
Итак, надеюсь теперь ни у кого не осталось сомнений в том, что eval НЕ БЕЗОПАСЕН, даже если убрать доступ к глобальным и встроенным переменным.
В примере выше мы использовали список всех подклассов класса object , чтобы создать объекты классов code и function . Точно таким же образом можно получить (и инстанцировать) любой класс, существующий в программе на момент вызова eval() .
Вот ещё один пример того, что можно сделать:
s = """ [ c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == "Quitter" ][0](0)() """ eval(s, >)
Модуль lib/site.py содержит класс Quitter , который вызывается интерпретатором, когда вы набираете quit() .
Код выше находит этот класс, инстанциирует его и вызывает, чем завершает работу интерпретатора.
Сейчас мы запускали eval в пустом окружении, исходя из того, что указанный в статье код — это весь код нашей программы.
В случае использования eval ‘а в реальном приложении злоумышленник может получить доступ ко всем классам, которые вы используете, так что его возможности не будут ограничены практически ничем.
Проблема всех подобных попыток сделать eval безопасным в том, что они все основаны на идее «чёрных списков», идее о том, что надо убрать доступ ко всем вещам, которые, как нам кажется, могут быть опасны при использовании в eval ‘е. С такой стратегией практически нет шансов на победу, ведь если окажется незапрещённым хоть что-то, система будет уязвима.
Когда я проводил исследование этой темы, я наткнулся на защищенный режим выполнения eval ‘а в Python, который является ещё одной попыткой побороть эту проблему:
>>> eval("(lambda:0).func_code", >) Traceback (most recent call last): File "", line 1, in File "", line 1, in RuntimeError: function attributes not accessible in restricted mode
Вкратце, он работает следующим образом: если __builtins__ внутри eval отличаются от «официальных» — eval переходит в защищенный режим, в котором закрыт доступ к некоторым опасным свойствам, таким как func_code у функций. Более подробное описание этого режима можно найти тут, но, как мы уже видели выше, он тоже не является «серебряной пулей».
И всё-таки, можно ли сделать eval безопасным? Сложно сказать. Как мне кажется, злоумышленнику не удастся навредить без доступа к объектам с двумя нижними подчёркиваниями, обрамляющими имя, так что возможно, если исключить из обработки все строки с двумя нижними подчёркиваниями, то мы будем в безопасности. Возможно.
P.S.
В треде на Reddit я нашёл короткий сниппет, позволяющий нам в eval получить «оригинальные» __builtins__:
[ c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings' ][0]()._module.__builtins__
Как правильно написать калькулятор на питоне с помощью eval()
В комментариях к статьям по синтаксическому анализу я иногда вижу такие:
на питоне калькулятор пишется проще простого — print(eval(input()))
Ну, вобщем‑то — да, но если, например, вы прикрутите такой калькулятор к своему сайту, то любой желающий вместо 2+2*2 может написать exec(«import os; os.removedirs(‘/’)») , предварительно изучив все ваши секретные файлы подобным же образом. Такая перспектива не может радовать, но и отказываться от eval() тоже не стоит.
— А что делать‑то? — спросите вы. Ответ простой: валидировать входящие данные, как вы это всегда делаете. Только не те, которые передаются функции eval() — для этого вам действительно пришлось бы написать свой синтаксический анализатор, а внутренние данные, которыми оперирует реализация eval() .
— А! Я знаю — скажете вы — используем compile() , валидируем код и тогда уже вызываем eval() . Что-ж, возможно и так. Но я не знаю простого способа валидировать байт‑код. Нет. Нам надо валидировать результат питоновского синтаксического анализатора — абстрактное синтаксическое дерево или AST. Это гораздо проще, хоть и звучит пугающе.
Напишем свой eval() так:
def my_eval(expression): tree = ast.parse(expression, mode='eval') code = compile(tree, filename='', mode='eval') return eval(code)
Нам, естественнно, понадобится модуль ast:
>>> import ast
Проверяем — работает ли?
>>> print(my_eval(input())) 2+2*2 6
Итак, у нас есть tree . Вам, конечно же, любопытно, что там внутри, и действительно, надо же знать, что мы собираемся валидировать. Но просто так посмотреть не получится — это объект, и print() или pprint() выдадут всего лишь Я пользовался такой функцией:
def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0)
Можете вставить dump(tree) в my_eval(), поиграться и посмотреть, какое оно — это дерево. Только не надо копипастить мой пример с removedirs() .
Кому играться лениво — вот пара примеров:
>>> print(my_eval(input())) 2+2*2 AST Expression body AST BinOp left AST Constant value 2 kind None op AST Add right AST BinOp left AST Constant value 2 kind None op AST Mult right AST Constant value 2 kind None 6
>>> print(my_eval(input())) print(globals()) AST Expression body AST Call func AST Name id 'print' ctx AST Load args LIST list AST Call func AST Name id 'globals' ctx AST Load args LIST list keywords LIST list keywords LIST list , '__spec__': None, '__annotations__': <>, '__builtins__': , 'my_eval': , 'ast': , 'dump': > None
Видите, чем отличаются безопасные выражения от небезопасных? Безопасные содержат только Constant и BinOp , а небезопасные — всякие Call и Name . Полный список операций и других узлов дерева можно посмотреть в документации к модулю ast. Это поможет определиться, что запретить, а что разрешить. Для простого калькулятора, я считаю, достаточно разрешить основные операции BinOp и UnaryOp . Плюс Constant .
Пора писать валидатор:
_allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)
Вот так всё просто. Смело вызывайте эту функцию из my_eval() перед compile() :
def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') return eval(code)
и будет вам счастье. Но, имейте ввиду, документация к функции compile() сообщает:
Warning
It is possible to crash the Python interpreter with a sufficiently large/complex string when compiling to an AST object due to stack depth limitations in Python’s AST compiler.
Так что ограничивайте длину строки, передаваемой my_eval() . И тогда счастье будет настоящим.
— Постойте! — скажете вы — Мне не нужен простой бухгалтерский калькулятор! Где хоть какой-нибудь минимальный набор математических функций? Не отправлять же пользователя в пешее путешествие искать косинус фи?
М-да, пожалуй, соглашусь с вами. Давайте разрешим кое-что из модуля math . Но это будут вызовы функций, а значит — небезопасные области тьмы. Нам придётся добавить ast.Call , ast.Name и ast.Load в _allowed_nodes , и это открывает путь к exec() . Чтобы избежать неприятностей, надо ограничить контекст выполнения, в котором важно наличие пустого __builtins__ :
def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = > return eval(code, context)
Плюс, надо добавить поддержку узлов-списков в валидатор:
def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree)
Теперь, если мы введём
NameError: name ‘exec’ is not defined
Ура! Ничего лучшего я, конечно, придумать не могу, но надеюсь что те, кто умнее меня, напишут в комментариях как это взломать и, соответственно, защититься.
Итак, функции у нас разрешены, надо добавить нужные в контекст выполнения. Я ограничусь синусом и косинусом, остальное вы и сами сможете. По своему вкусу.
import math def my_eval(expression): tree = ast.parse(expression, mode='eval') validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context)
Может показаться, что достаточно создать контекст всего один раз, вне функции, как мы это сделали с _allowed_nodes в валидаторе, но! — _allowed_nodes у нас всё-таки immutable, а для контекста есть риск модификации, случайной или преднамеренной. Известные аналогичные грабли — использование dict и list в качестве значений по умолчанию для аргументов. Поэтому лучше создавать контекст каждый раз, внутри функции.
Ну, вот собственно, и всё. Пользуйтесь и наслаждайтесь. Для тех, кому лень собирать копипастой всё в кучу, ловите полный исходник:
import ast import math def my_eval(expression): tree = ast.parse(expression, mode='eval') #dump(tree) validate_ast(tree) code = compile(tree, filename='', mode='eval') context = < '__builtins__': <>, 'sin': math.sin, 'cos': math.cos > return eval(code, context) _allowed_nodes = ( # базовые узлы: ast.BinOp, ast.UnaryOp, ast.Constant, ast.Call, ast.Name, ast.Load, # основные BinOps: ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow, # основные UnaryOps: ast.UAdd, ast.USub ) def validate_ast(tree): # валидируем корень дерева if not isinstance(tree, ast.Expression): raise Exception('Неправильное выражение') # валидируем узлы def validate_children(node): for child in ast.iter_child_nodes(node): if isinstance(child, list): for grandchild in child: validate_children(grandchild) else: if not isinstance(child, _allowed_nodes): raise Exception('Неправильное выражение') validate_children(child) validate_children(tree) def dump(node): def _format(node, indent): if isinstance(node, ast.AST): print('%sAST %s' % (' ' * indent, node.__class__.__name__)) for a, b in ast.iter_fields(node): print('%s%s' % (' ' * indent, a)) _format(b, indent + 4) elif isinstance(node, list): print('%sLIST %s' % (' ' * indent, node.__class__.__name__)) for x in node: _format(x, indent) else: print('%s%s' % (' ' * indent, repr(node))) _format(node, 0) print(my_eval(input()))
- Python
- Программирование
