在文件 ai2_kit/domain.py 中
def fun(ctx):
def in_add(a, b):
print (a+b)
ctx.executor.run_python_fn(in_add)(1, 2) # this pass
ctx.executor.run_python_fn(out_add)(1, 2) # this failed, the error is: ModuleNotFoundError: No module named 'ai2_kit'
def out_add(a, b):
print(a+b)
方法
run_python_fn
定义在ai2_kit/executor.py中,基本思想是使用python -c
在远程机器上执行Python脚本。
def run_python_script(self, script: str):
return self.connector.run('python -c {}'.format(shlex.quote(script)))
def run_python_fn(self, fn: T, python_cmd=None) -> T:
def remote_fn(*args, **kwargs):
dumped_fn = base64.b64encode(cloudpickle.dumps(lambda: fn(*args, **kwargs), protocol=pickle.DEFAULT_PROTOCOL))
script = '''import base64,pickle; pickle.loads(base64.b64decode({}))()'''.format(repr(dumped_fn))
self.run_python_script(script=script, python_cmd=python_cmd)
我不知道为什么当使用当前函数之外的函数时它会导入
ai2_kit
,该方法out_add
没有任何外部依赖项。有什么方法可以解决这个问题吗?谢谢你!本地和远程Python都是v3.9.
我根据@jasonharper的解释找到了解决办法:
是普通in_add
无法处理的东西,因为没有全局定义的名称;pickle
认识到这一事实,并对实际的函数定义进行编码,而不仅仅是名称。cloudpickle
是正常的out_add
可以处理得很好的东西(假设相同的模块可用于执行 unpickling 的程序),所以pickle
认为它不需要做任何特殊的事情。cloudpickle
只需将
out_add
的定义更改为
def __export_remote_functions():
def add(a, b):
return a + b
def prod(a, b):
return a * b
return (add, prod)
(add, prod) = __export_remote_functions()
executor.run_python_fn(add)(1, 2)
现在一切都很好。
对于要远程执行的函数应该这样定义,并且需要确保它们不能直接依赖于本地模块中定义的任何函数或类(全局模块也可以,只需确保它们也安装在远程Python中即可)。
一个好的做法是在您的项目下创建一个专用的
remote
包,并确保包中的所有方法和类都通过这种特殊方式定义,并且不要导入 remote
包之外的任何内容。