代码之家  ›  专栏  ›  技术社区  ›  jez

假设使用Unicode文本,如何安全地评估文本的表示?

  •  3
  • jez  · 技术社区  · 6 年前

    在Python2中,我想计算一个包含文本表示的字符串。我想安全地做这个,所以我不想用 eval() 相反,我已经习惯了使用 ast.literal_eval() 对于这种任务。

    不过,我还想在假设纯引号中的字符串文字表示 unicode 对象也就是说,你得到的那种向前兼容的行为 from __future__ import unicode_literals . 在下面的示例中, 表达式() 似乎尊重这种偏好,但是 ast.literal评估() 似乎没有。

    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 回复  |  直到 6 年前
        1
  •  4
  •   user2357112    6 年前

    既不 ast.literal_eval 也不 ast.parse 提供设置编译器标志的选项。您可以将适当的标志传递给 compile 分析字符串 unicode_literals 激活,然后运行 ast.literal评估 在结果节点上:

    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)
    
        2
  •  4
  •   wim    6 年前

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

    def my_literal_eval(s):
        ast.literal_eval(s)
        return eval(s)
    
        3
  •  1
  •   blhsing    6 年前

    使代码潜在不安全的是对名称和/或属性的引用。您可以子类 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)