我有一个用于CPython的C扩展模块。我需要多个uWSGI工作人员共享该模块中对象的单个实例。我正在使用multiprocessing.BaseManager
的自定义子类来完成此工作,该子类基于this answer,它描述了一个非常相似的解决方案。
下面的第一个脚本是wifi.manager
(wifi.controller.IFace
是要共享的对象)。我使用python3 wifi/manager.py
运行它,然后启动Web服务器,该Web服务器在第二个片段中运行代码以获取共享对象实例。
wifi / manager.py:
#!/usr/bin/env python3
from multiprocessing.managers import BaseManager
# .register() changes the class itself. We don't want to do that to BaseManager.
class WifiManager(BaseManager):
pass
if __name__ == '__main__':
# If we are executed as a script (python3 manager.py), start the server
import atexit
from multiprocessing import Lock
import wifi.controller
ifaces_lock = Lock()
ifaces = dict()
def get_iface(iface_path):
with ifaces_lock:
if iface_path not in ifaces:
# Control interface isn't open. Open it.
iface = wifi.controller.IFace(iface_path)
ifaces[iface_path] = iface
return ifaces[iface_path]
def close_ifaces():
for iface in ifaces.values():
iface.close()
WifiManager.register('get_iface', get_iface)
atexit.register(close_ifaces)
manager = WifiManager(address=('127.0.0.1', 2437), authkey=b'wifimanager')
server = manager.get_server()
server.serve_forever()
else:
# If we are imported, provide the WifiManager class ready for clients to use
WifiManager.register('get_iface')
网络应用程序中的代码段
from wifi.manager import WifiManager
...
wmanager = WifiManager(address=('127.0.0.1', 2437), authkey=b'wifimanager')
wmanager.connect()
iface = wmanager.get_iface(iface_path)
iface.scan() # And other code using the iface object
wifi.controller.IFace
对象有时会引发异常,无论是内置异常(主要是OSError
)还是其自身的wifi.controller.WifiError
异常。有时,我希望能够在Web应用程序中捕获这些错误,以便向客户端显示有意义的错误页面。但是,我注意到有时会捕获这些异常,并且在Web应用程序中会引发相同的异常(例如WifiError
)。有时,Web应用程序会获取multiprocessing.managers.RemoteError
,并将来自管理器的回溯存储为字符串。
问题是,我怎么知道何时将引发原始异常以及何时将引发RemoteError
,所以我知道该抓哪个? All the Python docs say是这样:
如果调用引发异常,则_callmethod()重新引发。如果管理器流程中引发了其他异常,则该异常将转换为RemoteError异常,并由_callmethod()引发。
这对我来说不是很清楚,我无法弄清楚它与我观察到的行为如何吻合。
答案 0 :(得分:0)
我相信我已经解决了。我仍然不确定100%,但是鉴于这个问题没有引起多少关注,我认为我会继续并添加答案,以防将来的读者偶然发现此问题。
在原始远程调用过程中在WifiManager
中发生的异常被提升为RemoteError
。在这种情况下,这意味着在Web应用程序的这一行中,远程端发生了异常:
iface = wmanager.get_iface(iface_path)
此后,Web应用程序不再与WifiManager
进行直接交互。它仅与proxy object iface
进行交互。如果代理对象(或更准确地说,其引用对象)在远程端引发异常,则在Web应用程序中引发同一异常,而不是RemoteError
。因此,这意味着这条线是否会导致WifiError
或OSError
,这些是您会发现的异常:
iface.scan() # And other code using the iface object
因此,总而言之,当远端出现异常时,对BaseManager
(或子类)的调用会引发RemoteError
。调用代理对象会引发原始异常的副本。
请记住,这个答案是基于我的观察结果,而不是我对情况的完全而全面的了解,因此这里可能有一些我不知道的陷阱。如果有人知道更好,请纠正我。但这描述了我观察到的行为,并且似乎与问题中引用的文档一致。