什么时候multiprocessing.BaseManager使用RemoteError?

时间:2019-05-01 20:34:00

标签: python exception multiprocessing

我有一个用于CPython的C扩展模块。我需要多个uWSGI工作人员共享该模块中对象的单个实例。我正在使用multiprocessing.BaseManager的自定义子类来完成此工作,该子类基于this answer,它描述了一个非常相似的解决方案。

下面的第一个脚本是wifi.managerwifi.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()引发。

这对我来说不是很清楚,我无法弄清楚它与我观察到的行为如何吻合。

1 个答案:

答案 0 :(得分:0)

我相信我已经解决了。我仍然不确定100%,但是鉴于这个问题没有引起多少关注,我认为我会继续并添加答案,以防将来的读者偶然发现此问题。

在原始远程调用过程中在WifiManager中发生的异常被提升为RemoteError。在这种情况下,这意味着在Web应用程序的这一行中,远程端发生了异常:

iface = wmanager.get_iface(iface_path)

此后,Web应用程序不再与WifiManager进行直接交互。它仅与proxy object iface进行交互。如果代理对象(或更准确地说,其引用对象)在远程端引发异常,则在Web应用程序中引发同一异常,而不是RemoteError。因此,这意味着这条线是否会导致WifiErrorOSError,这些是您会发现的异常:

iface.scan() # And other code using the iface object

因此,总而言之,当远端出现异常时,对BaseManager(或子类)的调用会引发RemoteError。调用代理对象会引发原始异常的副本。

请记住,这个答案是基于我的观察结果,而不是我对情况的完全而全面的了解,因此这里可能有一些我不知道的陷阱。如果有人知道更好,请纠正我。但这描述了我观察到的行为,并且似乎与问题中引用的文档一致。