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

验证任意Python代码

  •  -1
  • powerisall  · 技术社区  · 8 年前

    我有一个应用程序,它将接收字符串,然后将其作为任意python代码运行。我希望在尝试运行该字符串并对其进行以下几方面的评估之前验证该字符串:

    1. 语法正确(这可以通过 compile(stringCode, foo.py, "execute") 内置)
    2. 所有进口产品均在本地提供
    3. 任意代码字符串中的类是否继承自特定类
    4. 来自#3的类是否也实现了一个特定命名的方法(以便稍后调用 foo.bar() 关于任意代码,没有太多麻烦)

    我已经查看了代码对象,但它们似乎不能做任何事情,除非我尝试直接运行代码,而我宁愿事先验证它是否工作

    1 回复  |  直到 8 年前
        1
  •  3
  •   Kevin    8 年前

    您可以使用 ast.parse 创建字符串的语法树。然后,您可以遍历树并验证您喜欢的任何解析时间质量。

    正如internet\u用户所说,这将 告诉您代码的运行时质量;如果模块是通过通常以外的机制导入的 import 声明,这些不会被验证。如果您的类被动态更改为添加或删除方法,那么您只需查看 defs 在他们的类定义中。

    如果您不担心这些,下面是一个示例实现:

    import ast
    import sys
    import os
    import imp
    
    s = """
    import math, gzip
    from os import system
    import numpy
    import obviouslyFakeModuleName
    
    class A(int):
        def troz(self):
            return 23
    
    class B(str):
        def zort(self):
            return 42
    """
    
    def can_be_imported(name):
        try:
            imp.find_module(name)
            return True
        except ImportError:
            return False
    
    def iter_nodes_by_type(code, type_or_types):
        for node in ast.walk(code):
            if isinstance(node, type_or_types):
                yield node
    
    def iter_imported_module_names(code):
        for node in iter_nodes_by_type(code, ast.Import):
            for alias in node.names:
                yield alias.name
        for node in iter_nodes_by_type(code, ast.ImportFrom):
            yield node.module
    
    def iter_globally_defined_classes(code):
        for child in ast.iter_child_nodes(code):
            if isinstance(child, ast.ClassDef):
                yield child
    
    def iter_methods(class_):
        for node in ast.iter_child_nodes(class_):
            if isinstance(node, ast.FunctionDef):
                yield node
    
    try:
        code = ast.parse(s)
    except SyntaxError:
        print("That string is not valid Python.")
        sys.exit(0)
    
    #inspection of imports
    for name in iter_imported_module_names(code):
        if can_be_imported(name):
            print("module {} is available for import.".format(name))
        else:
            print("module {} is not available for import.".format(name))
    
    #inspection of classes
    for class_ in iter_globally_defined_classes(code):
        class_name = class_.name
        base_class_names = [name.id for name in class_.bases]
        function_names = [func.name for func in iter_methods(class_)]
        print("Inspecting class {}...".format(class_name))
        #we want to know if this class inherits directly from int
        if "int" in base_class_names:
            print("  Does inherit from int.")
        else:
            print("  Does not inherit from int.")
    
        #and does it implement zort()?
        if "zort" in function_names:
            print("  Implements `zort`.")
        else:
            print("  Does not implement `zort`.")
    

    结果:

    module math is available for import.
    module gzip is available for import.
    module numpy is not available for import.
    module obviouslyFakeModuleName is not available for import.
    module os is available for import.
    Inspecting class A...
      Does inherit from int.
      Does not implement `zort`.
    Inspecting class B...
      Does not inherit from int.
      Implements `zort`.