如何将 python 解释器嵌入到用 Python 编写的应用程序中?

How to embed python interpreter into an app written in Python?

也许我在这里完全偏离了轨道(当然高于我的薪水等级),但我想做的是让我的应用程序的用户(我用 Python 编写,因为那是我的语言知道)一个 python 解释器来控制我的应用程序中的某些对象。许多 3D 和 VFX 软件(Maya、Blender、Nuke)都有类似的东西。这是我到目前为止得到的代码:

#main.py
import code
import networkx as nx

class Env():
    def __init__(self):
        self.graph = nx.graph.DiGraph()
        # load library with functions that will be availabel for user inside the app.
        import mylib
        functions = {f: getattr(mylib, f) for f in dir(mylib) if not f.startswith('__')}
        self._interpreter = code.InteractiveInterpreter(locals=functions)

    def execute_node(self, node=None):
        # In IRL the main object to be pass1ed to users' interpreter will be the self.graph object
        # but I made it more clear for this question.
        self._interpreter.locals['var'] = 42
        node = "print(var)\nprint(some_function())\nvar = 67" # Let's pretend node object contains this code.        
        self._interpreter.runcode(node)

if __name__ == '__main__':
    e = Env()
    # some code, node creation and so on...
    e.execute_code()
    print(e.locals['var'])
    


#mylib.py
var = None # I have to put this here because if there is no variable function fails at import
def some_function():
        print(var)

输出:

42 # This prints as expected
None # The print within the function prints the value that was there when module was initialized
67 # The last print returns expected value

因此,很明显 python 在第一次导入时解释函数并“烘焙”它在导入时拥有的全局变量。现在的问题是我能否以某种方式轻松地使其使用从 code.InteractiveInterpreter() 传递的全局变量,或者我应该寻找一个完全不同的解决方案(以及哪个解决方案):)?当然这个想法是两个 python 程序应该通信,用户应该使用一个特殊的库来操作软件,后端代码不应该暴露给他们。我说得通吗?谢谢:)

这是您确实要使用 exec() function 的 one-ish 实例,但是 请记住用户可以 运行 任何 Python代码,包括可能运行永远、搞乱你的主程序、写入(或删除)文件等的东西

def run_code(code, add_locals={}):
    code_locals = {}
    code_locals.update(add_locals)  # Copy in the additional locals so that dict could be reused
    exec(
      code, 
      {},  # no globals (you may wish to replace this),
      code_locals,
    )
    return code_locals  # return updated locals


class Beeper:  # define a toy object
    def beep(self, times):
        print("Beep! " * times)


beeper = Beeper()  # instantiate the object to play with

# Some user code...
user_code = """
x = 5
beeper.beep(x)
x += 3
"""

new_locals = run_code(user_code, {"beeper": beeper})
print(new_locals)

这输出

Beep! Beep! Beep! Beep! Beep!
{'beeper': <__main__.Beeper>, 'x': 8}

所以你可以看到我们可以在需要时使用用户修改的局部变量。