title: When does multiprocessing.BaseManager use RemoteError?
body: <p>I have a C extension module for CPython. I need multiple uWSGI workers to share a single instance of an object in this module. I am using a custom subclass of <code>multiprocessing.BaseManager</code> to accomplish this, based on <a href="https://stackoverflow.com/a/28426819/1239685">this answer</a> that describes a very similar solution.</p>
  
  <p>The first script below is <code>wifi.manager</code> (<code>wifi.controller.IFace</code> is the object to be shared). I run this with <code>python3 wifi/manager.py</code> and then start the web server, which runs the code in the second snippet to get the shared object instance.</p>
  
  <p>wifi/manager.py:</p>
  
  <pre class="lang-py prettyprint-override"><code>#!/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')
  </code></pre>
  
  <p>Snippet from web application:</p>
  
  <pre class="lang-py prettyprint-override"><code>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
  </code></pre>
  
  <p>The <code>wifi.controller.IFace</code> object sometimes raises exceptions, either built-in ones (mainly <code>OSError</code>) or its own <code>wifi.controller.WifiError</code> exception. Sometimes, I want to be able to catch these in the web app to present meaningful error pages to the client. But, what I've noticed is sometimes, these exceptions get captured and the same exception (e.g. <code>WifiError</code>) is raised in the web app. Other times, the web app gets a <code>multiprocessing.managers.RemoteError</code> with the traceback from the manager stored as a string.</p>
  
  <p>The question is, how do I know when it will raise the original exception and when it will raise a <code>RemoteError</code>, so I know which one to catch? <a href="https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod" rel="nofollow noreferrer">All the Python docs say</a> is this:</p>
  
  <blockquote>
    <p>If an exception is raised by the call, then is re-raised by _callmethod(). If some other exception is raised in the manager’s process then this is converted into a RemoteError exception and is raised by _callmethod().</p>
  </blockquote>
  
  <p>This isn't very clear to me and I can't figure out how it lines up with the behavior I've observed.</p>