Когда multiprocessing.BaseManager использует RemoteError? - PullRequest
0 голосов
/ 01 мая 2019

У меня есть модуль расширения C для CPython. Мне нужно несколько рабочих uWSGI, чтобы разделить один экземпляр объекта в этом модуле. Для этого я использую пользовательский подкласс multiprocessing.BaseManager, основанный на этом ответе , который описывает очень похожее решение.

Первый скрипт ниже wifi.manager (wifi.controller.IFace - это объект для совместного использования). Я запускаю это с python3 wifi/manager.py и затем запускаю веб-сервер, который запускает код во втором фрагменте для получения экземпляра общего объекта.

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. Иногда я хочу быть в состоянии перехватить их в веб-приложении, чтобы представить клиенту значимые страницы ошибок. Но иногда я замечал, что эти исключения регистрируются, и в веб-приложении возникает то же исключение (например, WifiError). В других случаях веб-приложение получает multiprocessing.managers.RemoteError с трассировкой от менеджера, сохраненной в виде строки.

Вопрос в том, как я узнаю, когда оно вызовет исходное исключение, а когда оно вызовет RemoteError, чтобы я знал, какое из них перехватить? Все документы Python говорят это:

Если исключение вызвано вызовом, то повторно вызывается _callmethod (). Если в процессе менеджера возникает какое-то другое исключение, оно преобразуется в исключение RemoteError и вызывается функцией _callmethod ().

Это не очень понятно для меня, и я не могу понять, как это согласуется с поведением, которое я наблюдал.

1 Ответ

0 голосов
/ 03 мая 2019

Мне кажется, я понял это. Я все еще не уверен на 100%, но, видя, что этому вопросу не уделяется много внимания, я подумал, что я продолжу и добавлю ответ, если будущие читатели наткнуться на это.

Исключения, которые происходят в WifiManager во время первоначального удаленного вызова, повышаются до RemoteError. В этом случае это означает исключения на удаленном конце во время этой строки в веб-приложении:

iface = wmanager.get_iface(iface_path)

После этого веб-приложение больше не взаимодействует напрямую с WifiManager. Он взаимодействует только с iface, прокси-объектом . Если прокси-объект (или, точнее, его референт) вызывает исключение на удаленной стороне, то же самое исключение возникает в веб-приложении, а не RemoteError. Таким образом, это означает, что если эта строка должна была вызвать, например, WifiError или OSError, вот исключения, которые вы бы поймали:

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

Итак, для подведения итогов, вызовы на BaseManager (или подклассах) повышают RemoteError, когда на удаленном конце есть исключение. При вызове прокси-объектов создается копия исходного исключения.

Имейте в виду, этот ответ основан на моих наблюдениях, а не на моем полном и полном понимании ситуации, поэтому возможно, что здесь есть некоторые ошибки, о которых я не знаю. Если кто-то знает лучше, пожалуйста, поправьте меня. Но это описывает поведение, которое я наблюдал, и кажется согласующимся с документами, приведенными в вопросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...