假设使用unicode_literals,如何安全地评估文字的表示形式?

时间:2019-01-16 20:22:35

标签: python unicode eval python-2.x literals

在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 :(得分:4)

ast.literal_evalast.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)

答案 1 :(得分:4)

一个有趣的问题。我不确定这里是否有ast.literal_eval的解决方案,但是我提供了一种便宜/安全的解决方法:

def my_literal_eval(s):
    ast.literal_eval(s)
    return eval(s)

答案 2 :(得分:1)

使代码可能不安全的原因是对名称和/或属性的引用。您可以对ast.NodeVisitor进行子类化,以确保在给定代码段eval之前没有这样的引用:

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)