如何将 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}
所以你可以看到我们可以在需要时使用用户修改的局部变量。
也许我在这里完全偏离了轨道(当然高于我的薪水等级),但我想做的是让我的应用程序的用户(我用 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}
所以你可以看到我们可以在需要时使用用户修改的局部变量。