# Buggy code
def get_server(
    panel,
    port=0,
    websocket_origin=None,
    loop=None,
    show=False,
    start=False,
    title=None,
    verbose=False,
    location=True,
    static_dirs={},
    **kwargs,
):
    """
    Returns a Server instance with this panel attached as the root
    app.

    Arguments
    ---------
    panel: Viewable, function or {str: Viewable}
      A Panel object, a function returning a Panel object or a
      dictionary mapping from the URL slug to either.
    port: int (optional, default=0)
      Allows specifying a specific port
    websocket_origin: str or list(str) (optional)
      A list of hosts that can connect to the websocket.

      This is typically required when embedding a server app in
      an external web site.

      If None, "localhost" is used.
    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
      The tornado IOLoop to run the Server on
    show : boolean (optional, default=False)
      Whether to open the server in a new browser tab on start
    start : boolean(optional, default=False)
      Whether to start the Server
    title: str or {str: str} (optional, default=None)
      An HTML title for the application or a dictionary mapping
      from the URL slug to a customized title
    verbose: boolean (optional, default=False)
      Whether to report the address and port
    location : boolean or panel.io.location.Location
      Whether to create a Location component to observe and
      set the URL location.
    static_dirs: dict (optional, default={})
      A dictionary of routes and local paths to serve as static file
      directories on those routes
    kwargs: dict
      Additional keyword arguments to pass to Server instance

    Returns
    -------
    server : bokeh.server.server.Server
      Bokeh Server instance running this panel
    """
    from tornado.ioloop import IOLoop

    server_id = kwargs.pop("server_id", uuid.uuid4().hex)
    kwargs["extra_patterns"] = extra_patterns = kwargs.get("extra_patterns", [])
    if isinstance(panel, dict):
        apps = {}
        for slug, app in panel.items():
            if isinstance(title, dict):
                try:
                    title_ = title[slug]
                except KeyError:
                    raise KeyError(
                        "Keys of the title dictionnary and of the apps "
                        f"dictionary must match. No {slug} key found in the "
                        "title dictionnary."
                    )
            else:
                title_ = title
            slug = slug if slug.startswith("/") else "/" + slug
            if "flask" in sys.modules:
                from flask import Flask

                if isinstance(app, Flask):
                    wsgi = WSGIContainer(app)
                    if slug == "/":
                        raise ValueError("Flask apps must be served on a subpath.")
                    if not slug.endswith("/"):
                        slug += "/"
                    extra_patterns.append(
                        (
                            "^" + slug + ".*",
                            ProxyFallbackHandler,
                            dict(fallback=wsgi, proxy=slug),
                        )
                    )
                    continue
            apps[slug] = partial(_eval_panel, app, server_id, title_, location)
    else:
        apps = {"/": partial(_eval_panel, panel, server_id, title, location)}

    extra_patterns += get_static_routes(static_dirs)

    opts = dict(kwargs)
    if loop:
        loop.make_current()
        opts["io_loop"] = loop
    else:
        opts["io_loop"] = IOLoop.current()

    if "index" not in opts:
        opts["index"] = INDEX_HTML

    if websocket_origin:
        if not isinstance(websocket_origin, list):
            websocket_origin = [websocket_origin]
        opts["allow_websocket_origin"] = websocket_origin

    server = Server(apps, port=port, **opts)
    if verbose:
        address = server.address or "localhost"
        print("Launching server at http://%s:%s" % (address, server.port))

    state._servers[server_id] = (server, panel, [])

    if show:

        def show_callback():
            server.show("/")

        server.io_loop.add_callback(show_callback)

    def sig_exit(*args, **kwargs):
        server.io_loop.add_callback_from_signal(do_stop)

    def do_stop(*args, **kwargs):
        server.io_loop.stop()

    try:
        signal.signal(signal.SIGINT, sig_exit)
    except ValueError:
        pass  # Can't use signal on a thread

    if start:
        server.start()
        try:
            server.io_loop.start()
        except RuntimeError:
            pass
    return server


# Traceback (KeyError)
$ docker run -it --entrypoint=//bin/bash --rm python:3.7.7-stretch
root@4074b09e57fc:/# pip install panel ipython
Collecting panel
Downloading panel-0.9.5-py2.py3-none-any.whl (1.3 MB)
|████████████████████████████████| 1.3 MB 762 kB/s
Collecting ipython
Downloading ipython-7.15.0-py3-none-any.whl (783 kB)
|████████████████████████████████| 783 kB 2.0 MB/s
Collecting param>=1.9.3
Downloading param-1.9.3-py2.py3-none-any.whl (70 kB)
|████████████████████████████████| 70 kB 1.4 MB/s
Collecting pyviz-comms>=0.7.4
Downloading pyviz_comms-0.7.5-py2.py3-none-any.whl (10 kB)
Collecting pyct>=0.4.4
Downloading pyct-0.4.6-py2.py3-none-any.whl (12 kB)
Collecting bokeh>=2.0.0
Downloading bokeh-2.0.2.tar.gz (8.6 MB)
|████████████████████████████████| 8.6 MB 1.5 MB/s
Collecting markdown
Downloading Markdown-3.2.2-py3-none-any.whl (88 kB)
|████████████████████████████████| 88 kB 1.8 MB/s
Collecting tqdm
Downloading tqdm-4.46.1-py2.py3-none-any.whl (63 kB)
|████████████████████████████████| 63 kB 1.2 MB/s
Requirement already satisfied: setuptools>=18.5 in /usr/local/lib/python3.7/site-packages (from ipython) (47.1.1)
Collecting jedi>=0.10
Downloading jedi-0.17.0-py2.py3-none-any.whl (1.1 MB)
|████████████████████████████████| 1.1 MB 1.7 MB/s
Collecting prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0
Downloading prompt_toolkit-3.0.5-py3-none-any.whl (351 kB)
|████████████████████████████████| 351 kB 1.9 MB/s
Collecting pygments
Downloading Pygments-2.6.1-py3-none-any.whl (914 kB)
|████████████████████████████████| 914 kB 2.0 MB/s
Collecting pexpect; sys_platform != "win32"
Downloading pexpect-4.8.0-py2.py3-none-any.whl (59 kB)
|████████████████████████████████| 59 kB 2.3 MB/s
Collecting decorator
Downloading decorator-4.4.2-py2.py3-none-any.whl (9.2 kB)
Collecting traitlets>=4.2
Downloading traitlets-4.3.3-py2.py3-none-any.whl (75 kB)
|████████████████████████████████| 75 kB 2.4 MB/s
Collecting pickleshare
Downloading pickleshare-0.7.5-py2.py3-none-any.whl (6.9 kB)
Collecting backcall
Downloading backcall-0.2.0-py2.py3-none-any.whl (11 kB)
Collecting PyYAML>=3.10
Downloading PyYAML-5.3.1.tar.gz (269 kB)
|████████████████████████████████| 269 kB 2.1 MB/s
Collecting python-dateutil>=2.1
Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
|████████████████████████████████| 227 kB 2.1 MB/s
Collecting Jinja2>=2.7
Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
|████████████████████████████████| 125 kB 2.1 MB/s
Collecting numpy>=1.11.3
Downloading numpy-1.18.5-cp37-cp37m-manylinux1_x86_64.whl (20.1 MB)
|████████████████████████████████| 20.1 MB 2.8 MB/s
Collecting pillow>=4.0
Downloading Pillow-7.1.2-cp37-cp37m-manylinux1_x86_64.whl (2.1 MB)
|████████████████████████████████| 2.1 MB 2.7 MB/s
Collecting packaging>=16.8
Downloading packaging-20.4-py2.py3-none-any.whl (37 kB)
Collecting tornado>=5
Downloading tornado-6.0.4.tar.gz (496 kB)
|████████████████████████████████| 496 kB 2.1 MB/s
Collecting typing_extensions>=3.7.4
Downloading typing_extensions-3.7.4.2-py3-none-any.whl (22 kB)
Collecting importlib-metadata; python_version < "3.8"
Downloading importlib_metadata-1.6.1-py2.py3-none-any.whl (31 kB)
Collecting parso>=0.7.0
Downloading parso-0.7.0-py2.py3-none-any.whl (100 kB)
|████████████████████████████████| 100 kB 1.6 MB/s
Collecting wcwidth
Downloading wcwidth-0.2.4-py2.py3-none-any.whl (30 kB)
Collecting ptyprocess>=0.5
Downloading ptyprocess-0.6.0-py2.py3-none-any.whl (39 kB)
Collecting ipython-genutils
Downloading ipython_genutils-0.2.0-py2.py3-none-any.whl (26 kB)
Collecting six
Downloading six-1.15.0-py2.py3-none-any.whl (10 kB)
Collecting MarkupSafe>=0.23
Downloading MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl (27 kB)
Collecting pyparsing>=2.0.2
Downloading pyparsing-2.4.7-py2.py3-none-any.whl (67 kB)
|████████████████████████████████| 67 kB 1.8 MB/s
Collecting zipp>=0.5
Downloading zipp-3.1.0-py3-none-any.whl (4.9 kB)
Building wheels for collected packages: bokeh, PyYAML, tornado
Building wheel for bokeh (setup.py) ... done
Created wheel for bokeh: filename=bokeh-2.0.2-py3-none-any.whl size=9072535 sha256=92af4d4c38b6492b4801df51c3ab39a797e03c82ca40c8cac75aced96b17f95f
Stored in directory: /root/.cache/pip/wheels/5e/9a/e0/2ce591d3bc02114f16ae1cdd7a4e34ff4d3b99eca54eeee53a
Building wheel for PyYAML (setup.py) ... done
Created wheel for PyYAML: filename=PyYAML-5.3.1-cp37-cp37m-linux_x86_64.whl size=411549 sha256=2ba23d0576a1b8efe148f7e6e188a6c664c9d35344b7146be82263a39de35a3c
Stored in directory: /root/.cache/pip/wheels/5e/03/1e/e1e954795d6f35dfc7b637fe2277bff021303bd9570ecea653
Building wheel for tornado (setup.py) ... done
Created wheel for tornado: filename=tornado-6.0.4-cp37-cp37m-linux_x86_64.whl size=428633 sha256=86bcd8bfc85c4fa27c9424006dee442777a61091ab26909db3cca11a76412a9d
Stored in directory: /root/.cache/pip/wheels/7d/14/fa/d88fb5da77d813ea0ffca38a2ab2a052874e9e1142bad0b348
Successfully built bokeh PyYAML tornado
Installing collected packages: param, pyviz-comms, pyct, PyYAML, six, python-dateutil, MarkupSafe, Jinja2, numpy, pillow, pyparsing, packaging, tornado, typing-extensions, bokeh, zipp, importlib-metadata, markdown, tqdm, panel, parso, jedi, wcwidth, prompt-toolkit, pygments, ptyprocess, pexpect, decorator, ipython-genutils, traitlets, pickleshare, backcall, ipython
Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1 PyYAML-5.3.1 backcall-0.2.0 bokeh-2.0.2 decorator-4.4.2 importlib-metadata-1.6.1 ipython-7.15.0 ipython-genutils-0.2.0 jedi-0.17.0 markdown-3.2.2 numpy-1.18.5 packaging-20.4 panel-0.9.5 param-1.9.3 parso-0.7.0 pexpect-4.8.0 pickleshare-0.7.5 pillow-7.1.2 prompt-toolkit-3.0.5 ptyprocess-0.6.0 pyct-0.4.6 pygments-2.6.1 pyparsing-2.4.7 python-dateutil-2.8.1 pyviz-comms-0.7.5 six-1.15.0 tornado-6.0.4 tqdm-4.46.1 traitlets-4.3.3 typing-extensions-3.7.4.2 wcwidth-0.2.4 zipp-3.1.0
root@4074b09e57fc:/# ipython
Python 3.7.7 (default, Jun  9 2020, 18:17:41)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.15.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import panel as pn

In [2]: def view():
...:     return pn.pane.Markdown("# Hello World")
...:

In [3]: app_routes = {"hello-world": view}

In [4]: pn.serve(app_routes, port=14033, dev=False, title="Panel App", num_procs=0)
Launching server at http://localhost:14033
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/usr/local/lib/python3.7/asyncio/selector_events.py in _add_reader(self, fd, callback, *args)
255         try:
--> 256             key = self._selector.get_key(fd)
257         except KeyError:

/usr/local/lib/python3.7/selectors.py in get_key(self, fileobj)
191         except KeyError:
--> 192             raise KeyError("{!r} is not registered".format(fileobj)) from None
193

KeyError: '14 is not registered'

During handling of the above exception, another exception occurred:

FileExistsError                           Traceback (most recent call last)
<ipython-input-4-df09cb8ef7a7> in <module>
----> 1 pn.serve(app_routes, port=14033, dev=False, title="Panel App", num_procs=0)

/usr/local/lib/python3.7/site-packages/panel/io/server.py in serve(panels, port, websocket_origin, loop, show, start, title, verbose, **kwargs)
139     """
140     return get_server(panels, port, websocket_origin, loop, show, start,
--> 141                       title, verbose, **kwargs)
142
143

/usr/local/lib/python3.7/site-packages/panel/io/server.py in get_server(panel, port, websocket_origin, loop, show, start, title, verbose, **kwargs)
235         opts['allow_websocket_origin'] = websocket_origin
236
--> 237     server = Server(apps, port=port, **opts)
238     if verbose:
239         address = server.address or 'localhost'

/usr/local/lib/python3.7/site-packages/bokeh/server/server.py in __init__(self, applications, io_loop, http_server_kwargs, **kwargs)
402
403             http_server.start(opts.num_procs)
--> 404             http_server.add_sockets(sockets)
405
406         except Exception:

/usr/local/lib/python3.7/site-packages/tornado/tcpserver.py in add_sockets(self, sockets)
164             self._sockets[sock.fileno()] = sock
165             self._handlers[sock.fileno()] = add_accept_handler(
--> 166                 sock, self._handle_connection
167             )
168

/usr/local/lib/python3.7/site-packages/tornado/netutil.py in add_accept_handler(sock, callback)
277         removed[0] = True
278
--> 279     io_loop.add_handler(sock, accept_handler, IOLoop.READ)
280     return remove_handler
281

/usr/local/lib/python3.7/site-packages/tornado/platform/asyncio.py in add_handler(self, fd, handler, events)
98         self.handlers[fd] = (fileobj, handler)
99         if events &amp; IOLoop.READ:
--> 100             self.asyncio_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
101             self.readers.add(fd)
102         if events &amp; IOLoop.WRITE:

/usr/local/lib/python3.7/asyncio/selector_events.py in add_reader(self, fd, callback, *args)
327         """Add a reader callback."""
328         self._ensure_fd_no_transport(fd)
--> 329         return self._add_reader(fd, callback, *args)
330
331     def remove_reader(self, fd):

/usr/local/lib/python3.7/asyncio/selector_events.py in _add_reader(self, fd, callback, *args)
257         except KeyError:
258             self._selector.register(fd, selectors.EVENT_READ,
--> 259                                     (handle, None))
260         else:
261             mask, (reader, writer) = key.events, key.data

/usr/local/lib/python3.7/selectors.py in register(self, fileobj, events, data)
357             poller_events |= self._EVENT_WRITE
358         try:
--> 359             self._selector.register(key.fd, poller_events)
360         except:
361             super().unregister(fileobj)

FileExistsError: [Errno 17] File exists

# Fixed code
def get_server(
    panel,
    port=0,
    websocket_origin=None,
    loop=None,
    show=False,
    start=False,
    title=None,
    verbose=False,
    location=True,
    static_dirs={},
    **kwargs,
):
    """
    Returns a Server instance with this panel attached as the root
    app.

    Arguments
    ---------
    panel: Viewable, function or {str: Viewable}
      A Panel object, a function returning a Panel object or a
      dictionary mapping from the URL slug to either.
    port: int (optional, default=0)
      Allows specifying a specific port
    websocket_origin: str or list(str) (optional)
      A list of hosts that can connect to the websocket.

      This is typically required when embedding a server app in
      an external web site.

      If None, "localhost" is used.
    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
      The tornado IOLoop to run the Server on
    show : boolean (optional, default=False)
      Whether to open the server in a new browser tab on start
    start : boolean(optional, default=False)
      Whether to start the Server
    title: str or {str: str} (optional, default=None)
      An HTML title for the application or a dictionary mapping
      from the URL slug to a customized title
    verbose: boolean (optional, default=False)
      Whether to report the address and port
    location : boolean or panel.io.location.Location
      Whether to create a Location component to observe and
      set the URL location.
    static_dirs: dict (optional, default={})
      A dictionary of routes and local paths to serve as static file
      directories on those routes
    kwargs: dict
      Additional keyword arguments to pass to Server instance

    Returns
    -------
    server : bokeh.server.server.Server
      Bokeh Server instance running this panel
    """
    from tornado.ioloop import IOLoop

    server_id = kwargs.pop("server_id", uuid.uuid4().hex)
    kwargs["extra_patterns"] = extra_patterns = kwargs.get("extra_patterns", [])
    if isinstance(panel, dict):
        apps = {}
        for slug, app in panel.items():
            if isinstance(title, dict):
                try:
                    title_ = title[slug]
                except KeyError:
                    raise KeyError(
                        "Keys of the title dictionnary and of the apps "
                        f"dictionary must match. No {slug} key found in the "
                        "title dictionnary."
                    )
            else:
                title_ = title
            slug = slug if slug.startswith("/") else "/" + slug
            if "flask" in sys.modules:
                from flask import Flask

                if isinstance(app, Flask):
                    wsgi = WSGIContainer(app)
                    if slug == "/":
                        raise ValueError("Flask apps must be served on a subpath.")
                    if not slug.endswith("/"):
                        slug += "/"
                    extra_patterns.append(
                        (
                            "^" + slug + ".*",
                            ProxyFallbackHandler,
                            dict(fallback=wsgi, proxy=slug),
                        )
                    )
                    continue
            apps[slug] = partial(_eval_panel, app, server_id, title_, location)
    else:
        apps = {"/": partial(_eval_panel, panel, server_id, title, location)}

    extra_patterns += get_static_routes(static_dirs)

    opts = dict(kwargs)
    if loop:
        loop.make_current()
        opts["io_loop"] = loop
    elif opts.get("num_procs", 1) == 1:
        opts["io_loop"] = IOLoop.current()

    if "index" not in opts:
        opts["index"] = INDEX_HTML

    if websocket_origin:
        if not isinstance(websocket_origin, list):
            websocket_origin = [websocket_origin]
        opts["allow_websocket_origin"] = websocket_origin

    server = Server(apps, port=port, **opts)
    if verbose:
        address = server.address or "localhost"
        print("Launching server at http://%s:%s" % (address, server.port))

    state._servers[server_id] = (server, panel, [])

    if show:

        def show_callback():
            server.show("/")

        server.io_loop.add_callback(show_callback)

    def sig_exit(*args, **kwargs):
        server.io_loop.add_callback_from_signal(do_stop)

    def do_stop(*args, **kwargs):
        server.io_loop.stop()

    try:
        signal.signal(signal.SIGINT, sig_exit)
    except ValueError:
        pass  # Can't use signal on a thread

    if start:
        server.start()
        try:
            server.io_loop.start()
        except RuntimeError:
            pass
    return server