假设 unicode_literals,如何安全地评估文字的表示?
How can a representation of a literal be safely evaluated, assuming unicode_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
偏好?
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)
一个有趣的问题。我不确定这里是否有 ast.literal_eval
的解决方案,但我提供 cheap/safe 解决方法:
def my_literal_eval(s):
ast.literal_eval(s)
return eval(s)
使代码可能不安全的原因是对名称 and/or 属性的引用。您可以子类化 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)
在 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
偏好?
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)
一个有趣的问题。我不确定这里是否有 ast.literal_eval
的解决方案,但我提供 cheap/safe 解决方法:
def my_literal_eval(s):
ast.literal_eval(s)
return eval(s)
使代码可能不安全的原因是对名称 and/or 属性的引用。您可以子类化 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)