We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Panel.serve does not work for panel.Templates without instantiation
#### System Info

- Panel: 0.8.0
- Bokeh: 1.4.0
- Tornado 6.0.3
- Python: 3.7.4
- OS: Windows 8.1
- Browser: Chrome

#### My Pain

I'm trying to serve a list of apps and one of them uses the Panel Templating System.

If I provide a function that returns a Template to `pn.Serve` the app is not shown.

#### Additional Info

If I provide a function that returns a Column to `pn.Serve` the app is shown
If I provide an instance of the Template to `pn.Serve` the app is shown

#### Screenshot

![image](https://user-images.githubusercontent.com/42288570/76757319-ab46a100-6787-11ea-9c49-8508bbaad4a8.png)

#### Code

````bash
import holoviews as hv
import panel as pn

TEMPLATE = """
<!-- This template is inspired by
- Bokeh Template. See https://panel.pyviz.org/user_guide/Templates.html
-->
{% extends base %}

<!-- goes in head -->
{% block postamble %}
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% endblock %}

<!-- goes in body -->
{% block contents %}

<section id="page">
    <header>
        {{ embed(roots.header) }}
    </header>
    <main>
        {{ embed(roots.main) }}
    </main>
</section>

{% endblock %}
"""


class DashboardTemplate(pn.Template):
    """A Basic App Template"""

    def __init__(self, **params):
        template = TEMPLATE

        self.header = pn.Row()
        self.main = pn.Column()

        items = {
            "header": self.header,
            "main": self.main,
        }
        super().__init__(template=template, items=items, **params)


def app_template():
    app = DashboardTemplate()
    component = pn.Column("# App Template",)
    app.main[:] = [component]
    return app


def app_column():
    return pn.Column("# App Column")


APP_ROUTES = {
    "app_column": app_column,
    "app_template": app_template,
    "app_template_instance": app_template(),
}

pn.serve(APP_ROUTES, port=14033, dev=True)
````


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `panel/io/server.py`
Content:
```
1 """
2 Utilities for creating bokeh Server instances.
3 """
4 from __future__ import absolute_import, division, unicode_literals
5 
6 import os
7 import signal
8 import threading
9 import uuid
10 
11 from contextlib import contextmanager
12 from functools import partial
13 from types import FunctionType
14 
15 from bokeh.document.events import ModelChangedEvent
16 from bokeh.server.server import Server
17 from tornado.websocket import WebSocketHandler
18 
19 from .state import state
20 
21 
22 #---------------------------------------------------------------------
23 # Private API
24 #---------------------------------------------------------------------
25 
26 INDEX_HTML = os.path.join(os.path.dirname(__file__), '..', '_templates', "index.html")
27 
28 def _origin_url(url):
29     if url.startswith("http"):
30         url = url.split("//")[1]
31     return url
32 
33 
34 def _server_url(url, port):
35     if url.startswith("http"):
36         return '%s:%d%s' % (url.rsplit(':', 1)[0], port, "/")
37     else:
38         return 'http://%s:%d%s' % (url.split(':')[0], port, "/")
39 
40 def _eval_panel(panel, server_id, title, doc):
41     from ..template import Template
42     from ..pane import panel as as_panel
43 
44     if isinstance(panel, Template):
45         return panel._modify_doc(server_id, title, doc)
46     elif isinstance(panel, FunctionType):
47         panel = panel()
48     return as_panel(panel)._modify_doc(server_id, title, doc)
49     
50 #---------------------------------------------------------------------
51 # Public API
52 #---------------------------------------------------------------------
53 
54 
55 @contextmanager
56 def unlocked():
57     """
58     Context manager which unlocks a Document and dispatches
59     ModelChangedEvents triggered in the context body to all sockets
60     on current sessions.
61     """
62     curdoc = state.curdoc
63     if curdoc is None or curdoc.session_context is None:
64         yield
65         return
66     connections = curdoc.session_context.session._subscribed_connections
67 
68     hold = curdoc._hold
69     if hold:
70         old_events = list(curdoc._held_events)
71     else:
72         old_events = []
73         curdoc.hold()
74     try:
75         yield
76         events = []
77         for conn in connections:
78             socket = conn._socket
79             for event in curdoc._held_events:
80                 if (isinstance(event, ModelChangedEvent) and event not in old_events
81                     and hasattr(socket, 'write_message')):
82                     msg = conn.protocol.create('PATCH-DOC', [event])
83                     WebSocketHandler.write_message(socket, msg.header_json)
84                     WebSocketHandler.write_message(socket, msg.metadata_json)
85                     WebSocketHandler.write_message(socket, msg.content_json)
86                     for header, payload in msg._buffers:
87                         WebSocketHandler.write_message(socket, header)
88                         WebSocketHandler.write_message(socket, payload, binary=True)
89                 elif event not in events:
90                     events.append(event)
91         curdoc._held_events = events
92     finally:
93         if not hold:
94             curdoc.unhold()
95 
96 
97 def serve(panels, port=0, websocket_origin=None, loop=None, show=True,
98           start=True, title=None, verbose=True, **kwargs):
99     """
100     Allows serving one or more panel objects on a single server.
101     The panels argument should be either a Panel object or a function
102     returning a Panel object or a dictionary of these two. If a 
103     dictionary is supplied the keys represent the slugs at which
104     each app is served, e.g. `serve({'app': panel1, 'app2': panel2})`
105     will serve apps at /app and /app2 on the server.
106 
107     Arguments
108     ---------
109     panel: Viewable, function or {str: Viewable}
110       A Panel object, a function returning a Panel object or a
111       dictionary mapping from the URL slug to either.
112     port: int (optional, default=0)
113       Allows specifying a specific port
114     websocket_origin: str or list(str) (optional)
115       A list of hosts that can connect to the websocket.
116 
117       This is typically required when embedding a server app in
118       an external web site.
119 
120       If None, "localhost" is used.
121     loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
122       The tornado IOLoop to run the Server on
123     show : boolean (optional, default=False)
124       Whether to open the server in a new browser tab on start
125     start : boolean(optional, default=False)
126       Whether to start the Server
127     title: str (optional, default=None)
128       An HTML title for the application
129     verbose: boolean (optional, default=True)
130       Whether to print the address and port
131     kwargs: dict
132       Additional keyword arguments to pass to Server instance
133     """
134     return get_server(panels, port, websocket_origin, loop, show, start,
135                       title, verbose, **kwargs)
136 
137 
138 def get_server(panel, port=0, websocket_origin=None, loop=None,
139                show=False, start=False, title=None, verbose=False, **kwargs):
140     """
141     Returns a Server instance with this panel attached as the root
142     app.
143 
144     Arguments
145     ---------
146     panel: Viewable, function or {str: Viewable}
147       A Panel object, a function returning a Panel object or a
148       dictionary mapping from the URL slug to either.
149     port: int (optional, default=0)
150       Allows specifying a specific port
151     websocket_origin: str or list(str) (optional)
152       A list of hosts that can connect to the websocket.
153 
154       This is typically required when embedding a server app in
155       an external web site.
156 
157       If None, "localhost" is used.
158     loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())
159       The tornado IOLoop to run the Server on
160     show : boolean (optional, default=False)
161       Whether to open the server in a new browser tab on start
162     start : boolean(optional, default=False)
163       Whether to start the Server
164     title: str (optional, default=None)
165       An HTML title for the application
166     verbose: boolean (optional, default=False)
167       Whether to report the address and port
168     kwargs: dict
169       Additional keyword arguments to pass to Server instance
170 
171     Returns
172     -------
173     server : bokeh.server.server.Server
174       Bokeh Server instance running this panel
175     """
176     from tornado.ioloop import IOLoop
177 
178     server_id = kwargs.pop('server_id', uuid.uuid4().hex)
179     if isinstance(panel, dict):
180         apps = {slug if slug.startswith('/') else '/'+slug:
181                 partial(_eval_panel, p, server_id, title)
182                 for slug, p in panel.items()}
183     else:
184         apps = {'/': partial(_eval_panel, panel, server_id, title)}
185 
186     opts = dict(kwargs)
187     if loop:
188         loop.make_current()
189         opts['io_loop'] = loop
190     else:
191         opts['io_loop'] = IOLoop.current()
192 
193     if 'index' not in opts:
194         opts['index'] = INDEX_HTML
195 
196     if websocket_origin:
197         if not isinstance(websocket_origin, list):
198             websocket_origin = [websocket_origin]
199         opts['allow_websocket_origin'] = websocket_origin
200 
201     server = Server(apps, port=port, **opts)
202     if verbose:
203         address = server.address or 'localhost'
204         print("Launching server at http://%s:%s" % (address, server.port))
205 
206     state._servers[server_id] = (server, panel, [])
207 
208     if show:
209         def show_callback():
210             server.show('/')
211         server.io_loop.add_callback(show_callback)
212 
213     def sig_exit(*args, **kwargs):
214         server.io_loop.add_callback_from_signal(do_stop)
215 
216     def do_stop(*args, **kwargs):
217         server.io_loop.stop()
218 
219     try:
220         signal.signal(signal.SIGINT, sig_exit)
221     except ValueError:
222         pass # Can't use signal on a thread
223 
224     if start:
225         server.start()
226         try:
227             server.io_loop.start()
228         except RuntimeError:
229             pass
230     return server
231 
232 
233 class StoppableThread(threading.Thread):
234     """Thread class with a stop() method."""
235 
236     def __init__(self, io_loop=None, timeout=1000, **kwargs):
237         from tornado import ioloop
238         super(StoppableThread, self).__init__(**kwargs)
239         self._stop_event = threading.Event()
240         self.io_loop = io_loop
241         self._cb = ioloop.PeriodicCallback(self._check_stopped, timeout)
242         self._cb.start()
243 
244     def _check_stopped(self):
245         if self.stopped:
246             self._cb.stop()
247             self.io_loop.stop()
248 
249     def run(self):
250         if hasattr(self, '_target'):
251             target, args, kwargs = self._target, self._args, self._kwargs
252         else:
253             target, args, kwargs = self._Thread__target, self._Thread__args, self._Thread__kwargs
254         if not target:
255             return
256         bokeh_server = None
257         try:
258             bokeh_server = target(*args, **kwargs)
259         finally:
260             if isinstance(bokeh_server, Server):
261                 bokeh_server.stop()
262             if hasattr(self, '_target'):
263                 del self._target, self._args, self._kwargs
264             else:
265                 del self._Thread__target, self._Thread__args, self._Thread__kwargs
266 
267     def stop(self):
268         self._stop_event.set()
269 
270     @property
271     def stopped(self):
272         return self._stop_event.is_set()
273
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/panel/io/server.py b/panel/io/server.py
--- a/panel/io/server.py
+++ b/panel/io/server.py
@@ -41,10 +41,10 @@
     from ..template import Template
     from ..pane import panel as as_panel
 
+    if isinstance(panel, FunctionType):
+        panel = panel()
     if isinstance(panel, Template):
         return panel._modify_doc(server_id, title, doc)
-    elif isinstance(panel, FunctionType):
-        panel = panel()
     return as_panel(panel)._modify_doc(server_id, title, doc)
     
 #---------------------------------------------------------------------

{"golden_diff": "diff --git a/panel/io/server.py b/panel/io/server.py\n--- a/panel/io/server.py\n+++ b/panel/io/server.py\n@@ -41,10 +41,10 @@\n     from ..template import Template\n     from ..pane import panel as as_panel\n \n+    if isinstance(panel, FunctionType):\n+        panel = panel()\n     if isinstance(panel, Template):\n         return panel._modify_doc(server_id, title, doc)\n-    elif isinstance(panel, FunctionType):\n-        panel = panel()\n     return as_panel(panel)._modify_doc(server_id, title, doc)\n     \n #---------------------------------------------------------------------\n", "issue": "Panel.serve does not work for panel.Templates without instantiation\n#### System Info\r\n\r\n- Panel: 0.8.0\r\n- Bokeh: 1.4.0\r\n- Tornado 6.0.3\r\n- Python: 3.7.4\r\n- OS: Windows 8.1\r\n- Browser: Chrome\r\n\r\n#### My Pain\r\n\r\nI'm trying to serve a list of apps and one of them uses the Panel Templating System.\r\n\r\nIf I provide a function that returns a Template to `pn.Serve` the app is not shown.\r\n\r\n#### Additional Info\r\n\r\nIf I provide a function that returns a Column to `pn.Serve` the app is shown\r\nIf I provide an instance of the Template to `pn.Serve` the app is shown\r\n\r\n#### Screenshot\r\n\r\n![image](https://user-images.githubusercontent.com/42288570/76757319-ab46a100-6787-11ea-9c49-8508bbaad4a8.png)\r\n\r\n#### Code\r\n\r\n````bash\r\nimport holoviews as hv\r\nimport panel as pn\r\n\r\nTEMPLATE = \"\"\"\r\n<!-- This template is inspired by\r\n- Bokeh Template. See https://panel.pyviz.org/user_guide/Templates.html\r\n-->\r\n{% extends base %}\r\n\r\n<!-- goes in head -->\r\n{% block postamble %}\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\r\n{% endblock %}\r\n\r\n<!-- goes in body -->\r\n{% block contents %}\r\n\r\n<section id=\"page\">\r\n    <header>\r\n        {{ embed(roots.header) }}\r\n    </header>\r\n    <main>\r\n        {{ embed(roots.main) }}\r\n    </main>\r\n</section>\r\n\r\n{% endblock %}\r\n\"\"\"\r\n\r\n\r\nclass DashboardTemplate(pn.Template):\r\n    \"\"\"A Basic App Template\"\"\"\r\n\r\n    def __init__(self, **params):\r\n        template = TEMPLATE\r\n\r\n        self.header = pn.Row()\r\n        self.main = pn.Column()\r\n\r\n        items = {\r\n            \"header\": self.header,\r\n            \"main\": self.main,\r\n        }\r\n        super().__init__(template=template, items=items, **params)\r\n\r\n\r\ndef app_template():\r\n    app = DashboardTemplate()\r\n    component = pn.Column(\"# App Template\",)\r\n    app.main[:] = [component]\r\n    return app\r\n\r\n\r\ndef app_column():\r\n    return pn.Column(\"# App Column\")\r\n\r\n\r\nAPP_ROUTES = {\r\n    \"app_column\": app_column,\r\n    \"app_template\": app_template,\r\n    \"app_template_instance\": app_template(),\r\n}\r\n\r\npn.serve(APP_ROUTES, port=14033, dev=True)\r\n````\r\n\n", "before_files": [{"content": "\"\"\"\nUtilities for creating bokeh Server instances.\n\"\"\"\nfrom __future__ import absolute_import, division, unicode_literals\n\nimport os\nimport signal\nimport threading\nimport uuid\n\nfrom contextlib import contextmanager\nfrom functools import partial\nfrom types import FunctionType\n\nfrom bokeh.document.events import ModelChangedEvent\nfrom bokeh.server.server import Server\nfrom tornado.websocket import WebSocketHandler\n\nfrom .state import state\n\n\n#---------------------------------------------------------------------\n# Private API\n#---------------------------------------------------------------------\n\nINDEX_HTML = os.path.join(os.path.dirname(__file__), '..', '_templates', \"index.html\")\n\ndef _origin_url(url):\n    if url.startswith(\"http\"):\n        url = url.split(\"//\")[1]\n    return url\n\n\ndef _server_url(url, port):\n    if url.startswith(\"http\"):\n        return '%s:%d%s' % (url.rsplit(':', 1)[0], port, \"/\")\n    else:\n        return 'http://%s:%d%s' % (url.split(':')[0], port, \"/\")\n\ndef _eval_panel(panel, server_id, title, doc):\n    from ..template import Template\n    from ..pane import panel as as_panel\n\n    if isinstance(panel, Template):\n        return panel._modify_doc(server_id, title, doc)\n    elif isinstance(panel, FunctionType):\n        panel = panel()\n    return as_panel(panel)._modify_doc(server_id, title, doc)\n    \n#---------------------------------------------------------------------\n# Public API\n#---------------------------------------------------------------------\n\n\n@contextmanager\ndef unlocked():\n    \"\"\"\n    Context manager which unlocks a Document and dispatches\n    ModelChangedEvents triggered in the context body to all sockets\n    on current sessions.\n    \"\"\"\n    curdoc = state.curdoc\n    if curdoc is None or curdoc.session_context is None:\n        yield\n        return\n    connections = curdoc.session_context.session._subscribed_connections\n\n    hold = curdoc._hold\n    if hold:\n        old_events = list(curdoc._held_events)\n    else:\n        old_events = []\n        curdoc.hold()\n    try:\n        yield\n        events = []\n        for conn in connections:\n            socket = conn._socket\n            for event in curdoc._held_events:\n                if (isinstance(event, ModelChangedEvent) and event not in old_events\n                    and hasattr(socket, 'write_message')):\n                    msg = conn.protocol.create('PATCH-DOC', [event])\n                    WebSocketHandler.write_message(socket, msg.header_json)\n                    WebSocketHandler.write_message(socket, msg.metadata_json)\n                    WebSocketHandler.write_message(socket, msg.content_json)\n                    for header, payload in msg._buffers:\n                        WebSocketHandler.write_message(socket, header)\n                        WebSocketHandler.write_message(socket, payload, binary=True)\n                elif event not in events:\n                    events.append(event)\n        curdoc._held_events = events\n    finally:\n        if not hold:\n            curdoc.unhold()\n\n\ndef serve(panels, port=0, websocket_origin=None, loop=None, show=True,\n          start=True, title=None, verbose=True, **kwargs):\n    \"\"\"\n    Allows serving one or more panel objects on a single server.\n    The panels argument should be either a Panel object or a function\n    returning a Panel object or a dictionary of these two. If a \n    dictionary is supplied the keys represent the slugs at which\n    each app is served, e.g. `serve({'app': panel1, 'app2': panel2})`\n    will serve apps at /app and /app2 on the server.\n\n    Arguments\n    ---------\n    panel: Viewable, function or {str: Viewable}\n      A Panel object, a function returning a Panel object or a\n      dictionary mapping from the URL slug to either.\n    port: int (optional, default=0)\n      Allows specifying a specific port\n    websocket_origin: str or list(str) (optional)\n      A list of hosts that can connect to the websocket.\n\n      This is typically required when embedding a server app in\n      an external web site.\n\n      If None, \"localhost\" is used.\n    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())\n      The tornado IOLoop to run the Server on\n    show : boolean (optional, default=False)\n      Whether to open the server in a new browser tab on start\n    start : boolean(optional, default=False)\n      Whether to start the Server\n    title: str (optional, default=None)\n      An HTML title for the application\n    verbose: boolean (optional, default=True)\n      Whether to print the address and port\n    kwargs: dict\n      Additional keyword arguments to pass to Server instance\n    \"\"\"\n    return get_server(panels, port, websocket_origin, loop, show, start,\n                      title, verbose, **kwargs)\n\n\ndef get_server(panel, port=0, websocket_origin=None, loop=None,\n               show=False, start=False, title=None, verbose=False, **kwargs):\n    \"\"\"\n    Returns a Server instance with this panel attached as the root\n    app.\n\n    Arguments\n    ---------\n    panel: Viewable, function or {str: Viewable}\n      A Panel object, a function returning a Panel object or a\n      dictionary mapping from the URL slug to either.\n    port: int (optional, default=0)\n      Allows specifying a specific port\n    websocket_origin: str or list(str) (optional)\n      A list of hosts that can connect to the websocket.\n\n      This is typically required when embedding a server app in\n      an external web site.\n\n      If None, \"localhost\" is used.\n    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())\n      The tornado IOLoop to run the Server on\n    show : boolean (optional, default=False)\n      Whether to open the server in a new browser tab on start\n    start : boolean(optional, default=False)\n      Whether to start the Server\n    title: str (optional, default=None)\n      An HTML title for the application\n    verbose: boolean (optional, default=False)\n      Whether to report the address and port\n    kwargs: dict\n      Additional keyword arguments to pass to Server instance\n\n    Returns\n    -------\n    server : bokeh.server.server.Server\n      Bokeh Server instance running this panel\n    \"\"\"\n    from tornado.ioloop import IOLoop\n\n    server_id = kwargs.pop('server_id', uuid.uuid4().hex)\n    if isinstance(panel, dict):\n        apps = {slug if slug.startswith('/') else '/'+slug:\n                partial(_eval_panel, p, server_id, title)\n                for slug, p in panel.items()}\n    else:\n        apps = {'/': partial(_eval_panel, panel, server_id, title)}\n\n    opts = dict(kwargs)\n    if loop:\n        loop.make_current()\n        opts['io_loop'] = loop\n    else:\n        opts['io_loop'] = IOLoop.current()\n\n    if 'index' not in opts:\n        opts['index'] = INDEX_HTML\n\n    if websocket_origin:\n        if not isinstance(websocket_origin, list):\n            websocket_origin = [websocket_origin]\n        opts['allow_websocket_origin'] = websocket_origin\n\n    server = Server(apps, port=port, **opts)\n    if verbose:\n        address = server.address or 'localhost'\n        print(\"Launching server at http://%s:%s\" % (address, server.port))\n\n    state._servers[server_id] = (server, panel, [])\n\n    if show:\n        def show_callback():\n            server.show('/')\n        server.io_loop.add_callback(show_callback)\n\n    def sig_exit(*args, **kwargs):\n        server.io_loop.add_callback_from_signal(do_stop)\n\n    def do_stop(*args, **kwargs):\n        server.io_loop.stop()\n\n    try:\n        signal.signal(signal.SIGINT, sig_exit)\n    except ValueError:\n        pass # Can't use signal on a thread\n\n    if start:\n        server.start()\n        try:\n            server.io_loop.start()\n        except RuntimeError:\n            pass\n    return server\n\n\nclass StoppableThread(threading.Thread):\n    \"\"\"Thread class with a stop() method.\"\"\"\n\n    def __init__(self, io_loop=None, timeout=1000, **kwargs):\n        from tornado import ioloop\n        super(StoppableThread, self).__init__(**kwargs)\n        self._stop_event = threading.Event()\n        self.io_loop = io_loop\n        self._cb = ioloop.PeriodicCallback(self._check_stopped, timeout)\n        self._cb.start()\n\n    def _check_stopped(self):\n        if self.stopped:\n            self._cb.stop()\n            self.io_loop.stop()\n\n    def run(self):\n        if hasattr(self, '_target'):\n            target, args, kwargs = self._target, self._args, self._kwargs\n        else:\n            target, args, kwargs = self._Thread__target, self._Thread__args, self._Thread__kwargs\n        if not target:\n            return\n        bokeh_server = None\n        try:\n            bokeh_server = target(*args, **kwargs)\n        finally:\n            if isinstance(bokeh_server, Server):\n                bokeh_server.stop()\n            if hasattr(self, '_target'):\n                del self._target, self._args, self._kwargs\n            else:\n                del self._Thread__target, self._Thread__args, self._Thread__kwargs\n\n    def stop(self):\n        self._stop_event.set()\n\n    @property\n    def stopped(self):\n        return self._stop_event.is_set()\n", "path": "panel/io/server.py"}], "after_files": [{"content": "\"\"\"\nUtilities for creating bokeh Server instances.\n\"\"\"\nfrom __future__ import absolute_import, division, unicode_literals\n\nimport os\nimport signal\nimport threading\nimport uuid\n\nfrom contextlib import contextmanager\nfrom functools import partial\nfrom types import FunctionType\n\nfrom bokeh.document.events import ModelChangedEvent\nfrom bokeh.server.server import Server\nfrom tornado.websocket import WebSocketHandler\n\nfrom .state import state\n\n\n#---------------------------------------------------------------------\n# Private API\n#---------------------------------------------------------------------\n\nINDEX_HTML = os.path.join(os.path.dirname(__file__), '..', '_templates', \"index.html\")\n\ndef _origin_url(url):\n    if url.startswith(\"http\"):\n        url = url.split(\"//\")[1]\n    return url\n\n\ndef _server_url(url, port):\n    if url.startswith(\"http\"):\n        return '%s:%d%s' % (url.rsplit(':', 1)[0], port, \"/\")\n    else:\n        return 'http://%s:%d%s' % (url.split(':')[0], port, \"/\")\n\ndef _eval_panel(panel, server_id, title, doc):\n    from ..template import Template\n    from ..pane import panel as as_panel\n\n    if isinstance(panel, FunctionType):\n        panel = panel()\n    if isinstance(panel, Template):\n        return panel._modify_doc(server_id, title, doc)\n    return as_panel(panel)._modify_doc(server_id, title, doc)\n    \n#---------------------------------------------------------------------\n# Public API\n#---------------------------------------------------------------------\n\n\n@contextmanager\ndef unlocked():\n    \"\"\"\n    Context manager which unlocks a Document and dispatches\n    ModelChangedEvents triggered in the context body to all sockets\n    on current sessions.\n    \"\"\"\n    curdoc = state.curdoc\n    if curdoc is None or curdoc.session_context is None:\n        yield\n        return\n    connections = curdoc.session_context.session._subscribed_connections\n\n    hold = curdoc._hold\n    if hold:\n        old_events = list(curdoc._held_events)\n    else:\n        old_events = []\n        curdoc.hold()\n    try:\n        yield\n        events = []\n        for conn in connections:\n            socket = conn._socket\n            for event in curdoc._held_events:\n                if (isinstance(event, ModelChangedEvent) and event not in old_events\n                    and hasattr(socket, 'write_message')):\n                    msg = conn.protocol.create('PATCH-DOC', [event])\n                    WebSocketHandler.write_message(socket, msg.header_json)\n                    WebSocketHandler.write_message(socket, msg.metadata_json)\n                    WebSocketHandler.write_message(socket, msg.content_json)\n                    for header, payload in msg._buffers:\n                        WebSocketHandler.write_message(socket, header)\n                        WebSocketHandler.write_message(socket, payload, binary=True)\n                elif event not in events:\n                    events.append(event)\n        curdoc._held_events = events\n    finally:\n        if not hold:\n            curdoc.unhold()\n\n\ndef serve(panels, port=0, websocket_origin=None, loop=None, show=True,\n          start=True, title=None, verbose=True, **kwargs):\n    \"\"\"\n    Allows serving one or more panel objects on a single server.\n    The panels argument should be either a Panel object or a function\n    returning a Panel object or a dictionary of these two. If a \n    dictionary is supplied the keys represent the slugs at which\n    each app is served, e.g. `serve({'app': panel1, 'app2': panel2})`\n    will serve apps at /app and /app2 on the server.\n\n    Arguments\n    ---------\n    panel: Viewable, function or {str: Viewable}\n      A Panel object, a function returning a Panel object or a\n      dictionary mapping from the URL slug to either.\n    port: int (optional, default=0)\n      Allows specifying a specific port\n    websocket_origin: str or list(str) (optional)\n      A list of hosts that can connect to the websocket.\n\n      This is typically required when embedding a server app in\n      an external web site.\n\n      If None, \"localhost\" is used.\n    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())\n      The tornado IOLoop to run the Server on\n    show : boolean (optional, default=False)\n      Whether to open the server in a new browser tab on start\n    start : boolean(optional, default=False)\n      Whether to start the Server\n    title: str (optional, default=None)\n      An HTML title for the application\n    verbose: boolean (optional, default=True)\n      Whether to print the address and port\n    kwargs: dict\n      Additional keyword arguments to pass to Server instance\n    \"\"\"\n    return get_server(panels, port, websocket_origin, loop, show, start,\n                      title, verbose, **kwargs)\n\n\ndef get_server(panel, port=0, websocket_origin=None, loop=None,\n               show=False, start=False, title=None, verbose=False, **kwargs):\n    \"\"\"\n    Returns a Server instance with this panel attached as the root\n    app.\n\n    Arguments\n    ---------\n    panel: Viewable, function or {str: Viewable}\n      A Panel object, a function returning a Panel object or a\n      dictionary mapping from the URL slug to either.\n    port: int (optional, default=0)\n      Allows specifying a specific port\n    websocket_origin: str or list(str) (optional)\n      A list of hosts that can connect to the websocket.\n\n      This is typically required when embedding a server app in\n      an external web site.\n\n      If None, \"localhost\" is used.\n    loop : tornado.ioloop.IOLoop (optional, default=IOLoop.current())\n      The tornado IOLoop to run the Server on\n    show : boolean (optional, default=False)\n      Whether to open the server in a new browser tab on start\n    start : boolean(optional, default=False)\n      Whether to start the Server\n    title: str (optional, default=None)\n      An HTML title for the application\n    verbose: boolean (optional, default=False)\n      Whether to report the address and port\n    kwargs: dict\n      Additional keyword arguments to pass to Server instance\n\n    Returns\n    -------\n    server : bokeh.server.server.Server\n      Bokeh Server instance running this panel\n    \"\"\"\n    from tornado.ioloop import IOLoop\n\n    server_id = kwargs.pop('server_id', uuid.uuid4().hex)\n    if isinstance(panel, dict):\n        apps = {slug if slug.startswith('/') else '/'+slug:\n                partial(_eval_panel, p, server_id, title)\n                for slug, p in panel.items()}\n    else:\n        apps = {'/': partial(_eval_panel, panel, server_id, title)}\n\n    opts = dict(kwargs)\n    if loop:\n        loop.make_current()\n        opts['io_loop'] = loop\n    else:\n        opts['io_loop'] = IOLoop.current()\n\n    if 'index' not in opts:\n        opts['index'] = INDEX_HTML\n\n    if websocket_origin:\n        if not isinstance(websocket_origin, list):\n            websocket_origin = [websocket_origin]\n        opts['allow_websocket_origin'] = websocket_origin\n\n    server = Server(apps, port=port, **opts)\n    if verbose:\n        address = server.address or 'localhost'\n        print(\"Launching server at http://%s:%s\" % (address, server.port))\n\n    state._servers[server_id] = (server, panel, [])\n\n    if show:\n        def show_callback():\n            server.show('/')\n        server.io_loop.add_callback(show_callback)\n\n    def sig_exit(*args, **kwargs):\n        server.io_loop.add_callback_from_signal(do_stop)\n\n    def do_stop(*args, **kwargs):\n        server.io_loop.stop()\n\n    try:\n        signal.signal(signal.SIGINT, sig_exit)\n    except ValueError:\n        pass # Can't use signal on a thread\n\n    if start:\n        server.start()\n        try:\n            server.io_loop.start()\n        except RuntimeError:\n            pass\n    return server\n\n\nclass StoppableThread(threading.Thread):\n    \"\"\"Thread class with a stop() method.\"\"\"\n\n    def __init__(self, io_loop=None, timeout=1000, **kwargs):\n        from tornado import ioloop\n        super(StoppableThread, self).__init__(**kwargs)\n        self._stop_event = threading.Event()\n        self.io_loop = io_loop\n        self._cb = ioloop.PeriodicCallback(self._check_stopped, timeout)\n        self._cb.start()\n\n    def _check_stopped(self):\n        if self.stopped:\n            self._cb.stop()\n            self.io_loop.stop()\n\n    def run(self):\n        if hasattr(self, '_target'):\n            target, args, kwargs = self._target, self._args, self._kwargs\n        else:\n            target, args, kwargs = self._Thread__target, self._Thread__args, self._Thread__kwargs\n        if not target:\n            return\n        bokeh_server = None\n        try:\n            bokeh_server = target(*args, **kwargs)\n        finally:\n            if isinstance(bokeh_server, Server):\n                bokeh_server.stop()\n            if hasattr(self, '_target'):\n                del self._target, self._args, self._kwargs\n            else:\n                del self._Thread__target, self._Thread__args, self._Thread__kwargs\n\n    def stop(self):\n        self._stop_event.set()\n\n    @property\n    def stopped(self):\n        return self._stop_event.is_set()\n", "path": "panel/io/server.py"}]}