title: Running 2 bottle apps at the same time using multiprocess.Pool
body: <p>I came into a situation where I need to run 2 applications (on a different port) at the same time. I understand this is probably not the best idea/design but please bear with me.</p>
  
  <p>The main problem is that <code>bottle.run</code> is blocking. The obvious workaround was to use <code>multiprocessing.Pool</code>, but I guess it is not that obvious.</p>
  
  <p>This works (running the same app on 2 different ports):</p>
  
  <pre><code>from multiprocessing import Pool
  import bottle
  
  app = bottle.Bottle()
  
  @app.route('/')
  def index():
      return 'hi'
  
  
  def run_app(port):
      bottle.run(port=port)
  
  
  if __name__ == '__main__':
      pool = Pool(2)
  
      ps = [pool.apply_async(run_app, (port,))
            for port in (8081, 8082)]
  
      [p.get() for p in ps]  # YES, using a list comp for side-effect
  </code></pre>
  
  <p>Output is</p>
  
  <pre><code>Bottle v0.12.15 server starting up (using WSGIRefServer())...
  Listening on http://127.0.0.1:8081/
  Hit Ctrl-C to quit.
  
  Bottle v0.12.15 server starting up (using WSGIRefServer())...
  Listening on http://127.0.0.1:8082/
  Hit Ctrl-C to quit.
  </code></pre>
  
  <p>However, trying to use 2 different apps just does not work.</p>
  
  <pre><code>from multiprocessing import Pool
  import bottle
  
  app1 = bottle.Bottle()
  app2 = bottle.Bottle()
  apps = [app1, app2]
  
  
  @app1.route('/')
  def index_app1():
      return 'app1'
  
  
  @app2.route('/')
  def index_app2():
      return 'app2'
  
  
  def run_app(app, port):
      bottle.run(app, port=port)
  
  
  if __name__ == '__main__':
      pool = Pool(2)
  
      ps = [pool.apply_async(run_app, (app, port))
            for app, port in zip(apps, [8081, 8082])]
  
      [p.get() for p in ps]
  </code></pre>
  
  <p>This outputs</p>
  
  <pre><code>Traceback (most recent call last):
    File "test.py", line 29, in &lt;module&gt;
      [p.get() for p in ps]
    File "test.py", line 29, in &lt;listcomp&gt;
      [p.get() for p in ps]
    File "D:\Python37\Lib\multiprocessing\pool.py", line 657, in get
      raise self._value
    File "D:\Python37\Lib\multiprocessing\pool.py", line 431, in _handle_tasks
      put(task)
    File "D:\Python37\Lib\multiprocessing\connection.py", line 206, in send
      self._send_bytes(_ForkingPickler.dumps(obj))
    File "D:\Python37\Lib\multiprocessing\reduction.py", line 51, in dumps
      cls(buf, protocol).dump(obj)
  AttributeError: Can't pickle local object 'ConfigDict.__init__.&lt;locals&gt;.&lt;lambda&gt;'
  </code></pre>
  
  <p><strong>UPDATE</strong></p>
  
  <p>Changing the first example to:</p>
  
  <pre><code>.
  .
  .
  
  def run_app(app, port):
      # instead of bottle.run(port=port) which uses default_app()
      app.run(port=port) # or bottle.run(app=app, port=port)
  .
  .
  .
  ps = [pool.apply_async(run_app, (app, port))
        for port in (8081, 8082)]
  </code></pre>
  
  <p>is also causing the same error</p>