什么是最接近我可以使用不同的Python版本调用Python函数?
说我有两个文件:什么是最接近我可以使用不同的Python版本调用Python函数?
# spam.py
import library_Python3_only as l3
def spam(x,y)
return l3.bar(x).baz(y)
和
# beans.py
import library_Python2_only as l2
...
现在假设我想打电话给spam
从beans
内。这不是直接可能的,因为这两个文件都依赖于不兼容的Python版本。当然,我可以Popen
不同的蟒蛇的过程,但我怎么能传递的参数,并且没有太多的流解析疼痛检索结果?
下面是使用subprocess
和pickle
我实际测试过一个完整的示例实现。请注意,您需要明确使用协议版本2来在Python 3端进行酸洗(至少对于组合Python 3.5.2和Python 2.7.3)。
# py3bridge.py
import sys
import pickle
import importlib
import io
import traceback
import subprocess
class Py3Wrapper(object):
def __init__(self, mod_name, func_name):
self.mod_name = mod_name
self.func_name = func_name
def __call__(self, *args, **kwargs):
p = subprocess.Popen(['python3', '-m', 'py3bridge',
self.mod_name, self.func_name],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
stdout, _ = p.communicate(pickle.dumps((args, kwargs)))
data = pickle.loads(stdout)
if data['success']:
return data['result']
else:
raise Exception(data['stacktrace'])
def main():
try:
target_module = sys.argv[1]
target_function = sys.argv[2]
args, kwargs = pickle.load(sys.stdin.buffer)
mod = importlib.import_module(target_module)
func = getattr(mod, target_function)
result = func(*args, **kwargs)
data = dict(success=True, result=result)
except Exception:
st = io.StringIO()
traceback.print_exc(file=st)
data = dict(success=False, stacktrace=st.getvalue())
pickle.dump(data, sys.stdout.buffer, 2)
if __name__ == '__main__':
main()
Python的3模块(使用陈列柜里的pathlib
模块)使用spam.listdir
# beans.py
import py3bridge
delegate = py3bridge.Py3Wrapper('spam', 'listdir')
py3result = delegate('.')
print py3result
非常好。我遇到了一些有关环境变量的问题(Python2环境是嵌入到另一个程序中的本地安装,它覆盖了'PATH'等),但是可以通过用'['env'替换直接的'python3' Popen调用来解决这个问题。 ,'-i','bash','-l','-c','python3 -m py3bridge'+ self.mod_name +''+ self.func_name]'。 – leftaroundabout
@leftaroundabout还有'subprocess.Popen'的'env'参数,您可以在其中传递子进程的环境变量。 –
啊,但也可以用来简单地忽略给定的参数/恢复到登录默认值? – leftaroundabout
假设主叫方是Python3.5 +,你有机会获得一个更好的subprocess模块。也许你可以用户subprocess.run
,并且通过通过stdin和stdout,分别发送腌Python对象沟通。会有一些设置的事情,但没有分析就在你身边,或用绳子等碴
这里是Python2代码示例通过subprocess.Popen
p = subprocess.Popen(python3_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout, stderr = p.communicate(pickle.dumps(python3_args))
result = pickle.load(stdout)
其实我在我的例子中写错了版本:我真的希望从python2中调用python3。无论如何,我希望这没有真正的区别...当然,酸洗也是这个方向的作用?如果没有,只有调用者是Python3才有效的答案也会有帮助(也许我可以实际上扭转依赖)。 – leftaroundabout
使用pickled对象的选项仍然存在,只需设置stdin并读取子进程的stdout就会很困难,因为您必须使用subprocess.call或subprocess.Popen,它们的接口较为复杂。你仍然避免做手动解析,因为问题的症结仍然是调用pickle.dumps/loads,这只是一点点的代码。 –
@leftaroundabout在Python 2.4中添加了['subprocess'](https://docs.python.org/2/library/subprocess.html)模块,所以似乎没有一个原因可以解释为什么你不能在你的情况下使用它。 – ray
你可以创建一个简单的脚本,例如:
import sys
import my_wrapped_module
import json
params = sys.argv
script = params.pop(0)
function = params.pop(0)
print(json.dumps(getattr(my_wrapped_module, function)(*params)))
您可以调用它像:
pythonx.x wrapper.py myfunction param1 param2
这显然是安全隐患,但要小心。
另请注意,如果你的参数不是字符串或整数,你会遇到一些问题,所以可能考虑将参数传递为json字符串,并在包装中使用json.loads()
进行转换。
看起来不错。但是,当这些参数包含大量数据时,我不认为这是合适的?对于我现在心目中的应用程序来说,它可能没问题,但通常我会觉得不舒服通过命令行传递如此大的json编码的字符串。 – leftaroundabout
我自己会觉得不舒服!哈哈。也许它可以帮助,但最好的解决方案可能是使用'2to3'来转换你的python2库。 –
我肯定宁愿将所有东西都迁移到Python3,但这不是一个真正的选择,因为该特定的Python2引擎是嵌入在第三方C++项目中的修改版本。 – leftaroundabout
它可以使用multiprocessing.managers
模块要达到什么
# spam.py
import pathlib
def listdir(p):
return [str(c) for c in pathlib.Path(p).iterdir()]
Python的2模块你要。它确实需要少量的黑客攻击。
既然有要公开功能的模块,那么你需要创建一个Manager
可以创建为这些功能的代理。
管理器进程:
我已经重新定义spam
到包含两个函数调用add
和sub
。
# spam.py
def add(x, y):
return x + y
def sub(x, y):
return x - y
客户端进程使用由SpamManager
公开的py3函数。
from __future__ import print_function
from multiprocessing.managers import BaseManager
class SpamManager(BaseManager):
pass
SpamManager.register("get_spam")
m = SpamManager(address=('localhost', 50000), authkey=b'abc',
serializer='xmlrpclib')
m.connect()
spam = m.get_spam()
print("1 + 2 = ", spam.add(1, 2)) # prints 1 + 2 = 3
print("1 - 2 = ", spam.sub(1, 2)) # prints 1 - 2 = -1
spam.__name__ # Attribute Error -- spam is a module, but its __name__ attribute
# is not exposed
一旦建立,这个表单提供了一种访问函数和值的简单方法。它还允许以类似的方式使用这些函数和值,如果它们不是代理,则可以使用它们。最后,它允许您在服务器进程上设置密码,以便只有经过授权的进程才能访问管理器。经理长时间运行,也意味着您不必为每个函数调用启动一个新进程。
一个限制是我使用xmlrpclib
模块而不是pickle
在服务器和客户端之间来回发送数据。这是因为python2和python3对pickle
使用不同的协议。您可以通过将您自己的客户端添加到multiprocessing.managers.listener_client
来解决此问题,该协议使用商定的协议来进行酸洗对象。
是你在backports中的函数吗? –