Как можно безопасно оценить представление литерала, предполагая unicode_literals? - PullRequest
0 голосов
/ 16 января 2019

В Python 2 я хотел бы оценить строку, содержащую представление литерала. Я хотел бы сделать это безопасно, поэтому я не хочу использовать eval() - вместо этого я привык использовать ast.literal_eval() для такого рода задач.

Однако я также хочу оценить в предположении, что строковые литералы в простых кавычках обозначают unicode объекты - т.е. такое прямолинейное поведение, которое вы получаете с from __future__ import unicode_literals. В приведенном ниже примере eval(), кажется, уважает это предпочтение, но ast.literal_eval(), кажется, не соответствует.

from __future__ import unicode_literals, print_function

import ast

raw = r"""   'hello'    """

value = eval(raw.strip())
print(repr(value))
# Prints:
# u'hello'

value = ast.literal_eval(raw.strip())
print(repr(value))
# Prints:
# 'hello'

Обратите внимание, что я ищу замену literal_eval общего назначения - я не знаю заранее, что вывод обязательно является строковым объектом. Я хочу предположить, что raw - это представление произвольного литерала Python, который может быть строкой или содержать одну или несколько строк, или нет.

Есть ли способ получить лучшее из обоих миров: функция, которая надежно оценивает представления произвольных литералов Python и с учетом предпочтения unicode_literals?

Ответы [ 3 ]

0 голосов
/ 16 января 2019

интересный вопрос. Я не уверен, есть ли здесь какое-либо решение для ast.literal_eval, но я предлагаю дешевый / безопасный обходной путь:

def my_literal_eval(s):
    ast.literal_eval(s)
    return eval(s)
0 голосов
/ 16 января 2019

Что делает коды потенциально небезопасными, так это ссылки на имена и / или атрибуты. Вы можете создать подкласс ast.NodeVisitor, чтобы убедиться, что в данном фрагменте кода нет таких ссылок, прежде чем eval it:

import ast
from textwrap import dedent

class Validate(ast.NodeVisitor):
    def visit_Name(self, node):
        raise ValueError("Reference to name '%s' found in expression" % node.id)
    def visit_Attribute(self, node):
        raise ValueError("Reference to attribute '%s' found in expression" % node.attr)

Validate().visit(ast.parse(dedent(raw), '<inline>', 'eval'))
eval(raw)
0 голосов
/ 16 января 2019

Ни ast.literal_eval, ни ast.parse не предоставляют возможность устанавливать флаги компилятора. Вы можете передать соответствующие флаги compile, чтобы проанализировать строку с активированным unicode_literals, а затем запустить ast.literal_eval на полученном узле:

import ast

# Not a future statement. This imports the __future__ module, and has no special
# effects beyond that.
import __future__

unparsed = '"blah"'
parsed = compile(unparsed,
                 '<string>',
                 'eval',
                 ast.PyCF_ONLY_AST | __future__.unicode_literals.compiler_flag)
value = ast.literal_eval(parsed)
...