验证任意 Python 代码



我有一个应用程序,它将接收一个字符串,然后作为任意python代码运行。 我希望在尝试运行它并评估它的一些事情之前验证这个字符串:

  1. 语法正确(这可以通过内置compile(stringCode, foo.py, "execute")来完成(
  2. 所有进口均可在当地使用
  3. 任意代码字符串中的类是否继承自特定类
  4. #3 中的类是否也实现了专门命名的方法(因此我以后可以在任意代码上调用 foo.bar()而不会有太多麻烦(

我已经环顾了一下代码对象,但是除非我尝试直接运行代码,否则它们似乎无法执行任何操作,而我宁愿事先验证它是否有效

您可以使用

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

正如internet_user所说,这不会告诉你代码的运行时质量;如果模块是通过通常的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`.

最新更新