# Buggy code
def get_server(
    panel,
    port=0,
    address=None,
    websocket_origin=None,
    loop=None,
    show=False,
    start=False,
    title=None,
    verbose=False,
    location=True,
    static_dirs={},
    oauth_provider=None,
    oauth_key=None,
    oauth_secret=None,
    oauth_extra_params={},
    cookie_secret=None,
    oauth_encryption_key=None,
    **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
    address : str
      The address the server should listen on for HTTP requests.
    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.
    oauth_provider: str
      One of the available OAuth providers
    oauth_key: str (optional, default=None)
      The public OAuth identifier
    oauth_secret: str (optional, default=None)
      The client secret for the OAuth provider
    oauth_extra_params: dict (optional, default={})
      Additional information for the OAuth provider
    cookie_secret: str (optional, default=None)
      A random secret string to sign cookies (required for OAuth)
    oauth_encryption_key: str (optional, default=False)
      A random encryption key used for encrypting OAuth user
      information and access tokens.
    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)}

    dist_dir = os.path.join(os.path.split(os.path.dirname(__file__))[0], "dist")
    static_dirs = dict(static_dirs, panel_dist=dist_dir)

    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 address is not None:
        opts["address"] = address

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

    # Configure OAuth
    from ..config import config

    if config.oauth_provider:
        from ..auth import OAuthProvider

        opts["auth_provider"] = OAuthProvider()
    if oauth_provider:
        config.oauth_provider = oauth_provider
    if oauth_key:
        config.oauth_key = oauth_key
    if oauth_extra_params:
        config.oauth_extra_params = oauth_extra_params
    if cookie_secret:
        config.cookie_secret = cookie_secret
    opts["cookie_secret"] = config.cookie_secret

    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("/login" if config.oauth_provider else "/")

        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 (RuntimeError)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-15-da8b0df4fb70> in <module>
----> 1 color_mapper.update(high=100)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/has_props.py in update(self, **kwargs)
368         '''
369         for k,v in kwargs.items():
--> 370             setattr(self, k, v)
371
372     def update_from_json(self, json_attributes, models=None, setter=None):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/has_props.py in __setattr__(self, name, value)
272
273         if name in props or (descriptor is not None and descriptor.fset is not None):
--> 274             super().__setattr__(name, value)
275         else:
276             matches, text = difflib.get_close_matches(name.lower(), props), "similar"

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in __set__(self, obj, value, setter)
537             raise RuntimeError("%s.%s is a readonly property" % (obj.__class__.__name__, self.name))
538
--> 539         self._internal_set(obj, value, setter=setter)
540
541     def __delete__(self, obj):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _internal_set(self, obj, value, hint, setter)
761
762         old = self.__get__(obj, obj.__class__)
--> 763         self._real_set(obj, old, value, hint=hint, setter=setter)
764
765     def _real_set(self, obj, old, value, hint=None, setter=None):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _real_set(self, obj, old, value, hint, setter)
830
831         # for notification purposes, "old" should be the logical old
--> 832         self._trigger(obj, old, value, hint=hint, setter=setter)
833
834     # called when a container is mutated "behind our back" and

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/core/property/descriptors.py in _trigger(self, obj, old, value, hint, setter)
907         '''
908         if hasattr(obj, 'trigger'):
--> 909             obj.trigger(self.name, old, value, hint, setter)
910
911

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/model.py in trigger(self, attr, old, new, hint, setter)
659                     self._document._invalidate_all_models()
660         # chain up to invoke callbacks
--> 661         super().trigger(attr, old, new, hint=hint, setter=setter)
662
663     def _attach_document(self, doc):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/util/callback_manager.py in trigger(self, attr, old, new, hint, setter)
155                     callback(attr, old, new)
156         if hasattr(self, '_document') and self._document is not None:
--> 157             self._document._notify_change(self, attr, old, new, hint, setter, invoke)
158         else:
159             invoke()

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _notify_change(self, model, attr, old, new, hint, setter, callback_invoker)
1040
1041         event = ModelChangedEvent(self, model, attr, old, new, serializable_new, hint, setter, callback_invoker)
-> 1042         self._trigger_on_change(event)
1043
1044     def _push_all_models_freeze(self):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _trigger_on_change(self, event)
1135             for cb in self._callbacks.values():
1136                 cb(event)
-> 1137         self._with_self_as_curdoc(invoke_callbacks)
1138
1139     def _with_self_as_curdoc(self, f):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in _with_self_as_curdoc(self, f)
1148             else:
1149                 set_curdoc(self)
-> 1150             return f()
1151         finally:
1152             set_curdoc(old_doc)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in invoke_callbacks()
1134         def invoke_callbacks():
1135             for cb in self._callbacks.values():
-> 1136                 cb(event)
1137         self._with_self_as_curdoc(invoke_callbacks)
1138

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/document.py in <lambda>(event)
702     def on_change_dispatch_to(self, receiver):
703         if not receiver in self._callbacks:
--> 704             self._callbacks[receiver] = lambda event: event.dispatch(receiver)
705
706     def on_session_destroyed(self, *callbacks):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/events.py in dispatch(self, receiver)
267
268         '''
--> 269         super().dispatch(receiver)
270         if hasattr(receiver, '_document_model_changed'):
271             receiver._document_model_changed(self)

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/document/events.py in dispatch(self, receiver)
122         super().dispatch(receiver)
123         if hasattr(receiver, '_document_patched'):
--> 124             receiver._document_patched(self)
125
126     def generate(self, references, buffers):

~/miniconda3/envs/PyX/lib/python3.7/site-packages/bokeh/server/session.py in _document_patched(self, event)
216
217         if self._pending_writes is None:
--> 218             raise RuntimeError("_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes")
219
220         # TODO (havocp): our "change sync" protocol is flawed because if both

RuntimeError: _pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes

# Fixed code
def get_server(
    panel,
    port=0,
    address=None,
    websocket_origin=None,
    loop=None,
    show=False,
    start=False,
    title=None,
    verbose=False,
    location=True,
    static_dirs={},
    oauth_provider=None,
    oauth_key=None,
    oauth_secret=None,
    oauth_extra_params={},
    cookie_secret=None,
    oauth_encryption_key=None,
    **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
    address : str
      The address the server should listen on for HTTP requests.
    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.
    oauth_provider: str
      One of the available OAuth providers
    oauth_key: str (optional, default=None)
      The public OAuth identifier
    oauth_secret: str (optional, default=None)
      The client secret for the OAuth provider
    oauth_extra_params: dict (optional, default={})
      Additional information for the OAuth provider
    cookie_secret: str (optional, default=None)
      A random secret string to sign cookies (required for OAuth)
    oauth_encryption_key: str (optional, default=False)
      A random encryption key used for encrypting OAuth user
      information and access tokens.
    kwargs: dict
      Additional keyword arguments to pass to Server instance.

    Returns
    -------
    server : bokeh.server.server.Server
      Bokeh Server instance running this panel
    """
    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)}

    dist_dir = os.path.join(os.path.split(os.path.dirname(__file__))[0], "dist")
    static_dirs = dict(static_dirs, panel_dist=dist_dir)

    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 address is not None:
        opts["address"] = address

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

    # Configure OAuth
    from ..config import config

    if config.oauth_provider:
        from ..auth import OAuthProvider

        opts["auth_provider"] = OAuthProvider()
    if oauth_provider:
        config.oauth_provider = oauth_provider
    if oauth_key:
        config.oauth_key = oauth_key
    if oauth_extra_params:
        config.oauth_extra_params = oauth_extra_params
    if cookie_secret:
        config.cookie_secret = cookie_secret
    opts["cookie_secret"] = config.cookie_secret

    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("/login" if config.oauth_provider else "/")

        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