예제 #1
0
def BokehApplication(applications, server, prefix="/", template_variables={}):
    prefix = "/" + prefix.strip("/") + "/" if prefix else "/"

    extra = {"prefix": prefix, **template_variables}

    funcs = {k: functools.partial(v, server, extra) for k, v in applications.items()}
    apps = {k: Application(FunctionHandler(v)) for k, v in funcs.items()}

    kwargs = dask.config.get("distributed.scheduler.dashboard.bokeh-application").copy()
    extra_websocket_origins = create_hosts_allowlist(
        kwargs.pop("allow_websocket_origin"), server.http_server.port
    )

    return BokehTornado(
        apps,
        prefix=prefix,
        use_index=False,
        extra_websocket_origins=extra_websocket_origins,
        **kwargs,
    )
    def test_request_server_info(self):
        application = Application()
        with ManagedServerLoop(application) as server:
            session = ClientSession(session_id='test_request_server_info',
                                    websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            assert session.document is None

            info = session.request_server_info()

            from bokeh import __version__

            assert info['version_info']['bokeh'] == __version__
            assert info['version_info']['server'] == __version__

            session.close()
            session.loop_until_closed(suppress_warning=True)
            assert not session.connected
예제 #3
0
 def __spawn_server(self):
     bslg = logging.getLogger('bokeh.server.util')
     bsll = bslg.getEffectiveLevel()
     bslg.setLevel(logging.ERROR)
     self._server_info['application'] = app = Application(
         FunctionHandler(self.__entry_point))
     app.add(BokehSessionHandler())
     self._server_info['server'] = srv = Server(
         {'/': app},
         io_loop=IOLoop.instance(),
         port=0,
         allow_websocket_origin=['*'])
     self._server_info['server_id'] = srv_id = uuid4().hex
     curstate().uuid_to_server[srv_id] = srv
     srv_addr = srv.address if srv.address else socket.gethostbyname(
         socket.gethostname())
     self._server_info['server_url'] = 'http://{}:{}/'.format(
         srv_addr, srv.port)
     srv.start()
     bslg.setLevel(bsll)
예제 #4
0
    def test_request_server_info(self, ManagedServerLoop: MSL) -> None:
        application = Application()
        with ManagedServerLoop(application) as server:
            session = ClientSession(session_id=ID("test_request_server_info"),
                                    websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            assert session.document is None

            info = session.request_server_info()

            from bokeh import __version__

            assert info['version_info']['bokeh'] == __version__
            assert info['version_info']['server'] == __version__

            session.close()
            session._loop_until_closed()
            assert not session.connected
예제 #5
0
async def test__use_provided_session_autoload_token(ManagedServerLoop) -> None:
    application = Application()
    with ManagedServerLoop(application) as server:
        sessions = server.get_sessions('/')
        assert 0 == len(sessions)

        expected = 'foo'
        expected_token = generate_jwt_token(expected)
        response = await http_get(
            server.io_loop,
            autoload_url(server) + "&bokeh-token=" + expected_token)
        js = response.body
        token = extract_token_from_json(js)
        assert expected_token == token
        sessionid = get_session_id(token)
        assert expected == sessionid

        sessions = server.get_sessions('/')
        assert 1 == len(sessions)
        assert expected == sessions[0].id
def launch_server():

    context = zmq.Context()

    origins = ["localhost:{}".format(5006)]

    apps = {'/': Application(FunctionHandler(partial(make_document, context)))}
    server = Server(apps, port=5006)

    server.start()

    print('Opening Bokeh application on:')
    for entry in origins:
        print('\thttp://{}/'.format(entry))

    try:
        server.io_loop.start()
    except KeyboardInterrupt:
        print("terminating")
        server.io_loop.stop()
예제 #7
0
def main():
    curDir = os.path.dirname(__file__)
    plotData = pd.read_csv(os.path.join(curDir, 'test.csv'))
    """Launch the server and connect to it.
    """
    global x_values, y_values
    x_values = plotData['Area'].values
    y_values = plotData['% of F'].values

    print("Preparing a bokeh application.")
    io_loop = IOLoop.current()
    bokeh_app = Application(FunctionHandler(
        modify_doc))

    server = Server({"/": bokeh_app}, io_loop=io_loop)
    server.start()
    print("Opening Bokeh application on http://localhost:5006/")

    io_loop.add_callback(server.show, "/")
    io_loop.start()
예제 #8
0
def test_websocket_origins():
    application = Application()
    with ManagedServerLoop(application) as server:
        assert server._tornado.websocket_origins == set(["localhost:5006"])

    # OK this is a bit of a confusing mess. The user-facing arg for server is
    # "allow_websocket_origin" which gets converted to "extra_websocket_origins"
    # for BokehTornado, which is exposed as a property "websocket_origins"...
    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo"]) as server:
        assert server._tornado.websocket_origins == set(["foo:80"])

    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo:8080"]) as server:
        assert server._tornado.websocket_origins == set(["foo:8080"])

    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo:8080",
                                                   "bar"]) as server:
        assert server._tornado.websocket_origins == set(["foo:8080", "bar:80"])
예제 #9
0
    def test_host_whitelist_failure(self):
        application = Application()

        # failure bad host
        with ManagedServerLoop(application, host=["bad_host"]) as server:
            session = ClientSession(websocket_url=ws_url(server), io_loop = server.io_loop)
            session.connect()
            assert not session.connected
            session.close()
            session.loop_until_closed()

        with ManagedServerLoop(application, host=["bad_host:5006"]) as server:
            session = ClientSession(websocket_url=ws_url(server), io_loop = server.io_loop)
            session.connect()
            assert not session.connected
            session.close()
            session.loop_until_closed()

        # failure good host, bad port
        with ManagedServerLoop(application, host=["localhost:80"]) as server:
            session = ClientSession(websocket_url=ws_url(server), io_loop = server.io_loop)
            session.connect()
            assert not session.connected
            session.close()
            session.loop_until_closed()

        # failure good host, bad default port
        with ManagedServerLoop(application, host=["localhost"]) as server:
            session = ClientSession(websocket_url=ws_url(server), io_loop = server.io_loop)
            session.connect()
            assert not session.connected
            session.close()
            session.loop_until_closed()

        # failure with custom port
        with ManagedServerLoop(application, port=8080, host=["localhost:8081"]) as server:
            session = ClientSession(websocket_url=ws_url(server), io_loop = server.io_loop)
            session.connect()
            assert not session.connected
            session.close()
            session.loop_until_closed()
예제 #10
0
    def test_host_whitelist_success(self):
        application = Application()

        # succeed no host value with defaults
        with ManagedServerLoop(application, host=None) as server:
            session = ClientSession(websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            session.close()
            session.loop_until_closed()

        # succeed no host value with port
        with ManagedServerLoop(application, port=8080, host=None) as server:
            session = ClientSession(websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            session.close()
            session.loop_until_closed()

        # succeed matching host value
        with ManagedServerLoop(application, port=8080,
                               host=["localhost:8080"]) as server:
            session = ClientSession(websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            session.close()
            session.loop_until_closed()

        # succeed matching host value one of multiple
        with ManagedServerLoop(application,
                               port=8080,
                               host=["bad_host", "localhost:8080"]) as server:
            session = ClientSession(websocket_url=ws_url(server),
                                    io_loop=server.io_loop)
            session.connect()
            assert session.connected
            session.close()
            session.loop_until_closed()
예제 #11
0
def run_server(queue, start_signal, port, db_options):
    """
    Setup and run a server creating und continuously updating a dashboard.

    The main building of the dashboard is done in run_dashboard.
    Here this function is only turned into an Application and run in a server.

    Args:
        queue (Queue):
            queue to which originally the parameters DataFrame is supplied and
            to which later the updated parameter Series will be supplied.

        start_signal (Queue):
            empty queue. The minimization starts once it stops being empty.

        port (int):
            port at which to display the dashboard.

        db_options (dict):
            dictionary with options. see ``run_dashboard`` for details.

    """
    db_options = _process_db_options(db_options)
    asyncio.set_event_loop(asyncio.new_event_loop())

    apps = {
        "/":
        Application(
            FunctionHandler(
                partial(
                    run_dashboard,
                    queue=queue,
                    db_options=db_options,
                    start_signal=start_signal,
                )))
    }

    server = _setup_server(apps, port)

    server._loop.start()
    server.start()
예제 #12
0
    def start(self):
        def plotter(doc):
            # Base data
            x = [item['x'] for item in self.data]
            y = [item['y'] for item in self.data]

            ds = ColumnDataSource(data=dict(x=x, y=y))

            def update_plot():
                new = {}
                if len(self.data):
                    new = {
                        'x': [self.data[-1]['x']],
                        'y': [self.data[-1]['y']]
                    }

                last = {}
                if len(ds.data['x']):
                    last = {'x': [ds.data['x'][-1]], 'y': [ds.data['y'][-1]]}

                if new != last:
                    print('Displaying new data')
                    ds.stream(new)

            p = figure(plot_width=1200, plot_height=400)
            p.line('x', 'y', color="firebrick", line_width=2, source=ds)

            doc.add_periodic_callback(update_plot, 1000)

            doc.add_root(p)

        io_loop = IOLoop.current()
        bokeh_app = Application(FunctionHandler(plotter))
        server = Server({'/': bokeh_app}, io_loop=io_loop, host="*")

        server.start()

        print('Opening Bokeh application on http://localhost:5006/')

        io_loop.add_callback(server.show, '/')
        io_loop.start()
예제 #13
0
def main():
    # parse command line arguments
    args = parse_args()
    # set the logger
    log = logging.getLogger("dashmd")
    log.setLevel(loglevel.get(args.log))
    log.debug(f"Set log level to '{args.log}'")
    os.environ['BOKEH_PY_LOG_LEVEL'] = dashmd_loglevel_to_bokeh.get(args.log)
    os.environ['BOKEH_LOG_LEVEL'] = dashmd_loglevel_to_bokeh.get(args.log)
    log.debug(
        f"Set Bokeh log level to '{dashmd_loglevel_to_bokeh.get(args.log)}'")
    # start the server
    try:
        log.debug("Preparing the Bokeh server")
        # create tornado IO loop
        io_loop = IOLoop.current()
        # force bokeh to load resources from CDN (quick fix, not working with bokeh 1.4.0)
        os.environ['BOKEH_RESOURCES'] = 'cdn'
        # create app
        app_dir = os.path.dirname(os.path.realpath(__file__))
        bokeh_app = Application(
            DirectoryHandler(filename=app_dir,
                             argv=[args.default_dir, args.update, args.port]))
        # create server
        server = Server(
            {'/': bokeh_app},
            io_loop=io_loop,
            port=args.port,
            num_procs=1,
            allow_websocket_origin=[f'localhost:{args.port}'],
        )
    except OSError:
        log.error(
            f"[ERROR] Port {args.port} is already in use. Please specify a different one by using the --port flag."
        )
        sys.exit(1)

    server.start()
    log.info(f"Opening DashMD on http://localhost:{args.port}")
    server.io_loop.add_callback(server.show, "/")
    server.io_loop.start()
예제 #14
0
    def _try_start_web_server(self):
        static_path = os.path.join(os.path.dirname(__file__), 'static')

        handlers = dict()
        for p, h in _bokeh_apps.items():
            handlers[p] = Application(
                FunctionHandler(functools.partial(h, self._scheduler_ip)))

        handler_kwargs = {'scheduler_ip': self._scheduler_ip}
        extra_patterns = [('/static/(.*)', BokehStaticFileHandler, {
            'path': static_path
        })]
        for p, h in _web_handlers.items():
            extra_patterns.append((p, h, handler_kwargs))

        retrial = 5
        while retrial:
            try:
                if self._port is None:
                    use_port = get_next_port()
                else:
                    use_port = self._port

                self._server = Server(
                    handlers,
                    allow_websocket_origin=['*'],
                    address=self._host,
                    port=use_port,
                    extra_patterns=extra_patterns,
                    http_server_kwargs={'max_buffer_size': 2**32},
                )
                self._server.start()
                self._port = use_port
                logger.info('Mars UI started at %s:%d', self._host, self._port)
                break
            except OSError:
                if self._port is not None:
                    raise
                retrial -= 1
                if retrial == 0:
                    raise
예제 #15
0
    def test_host_whitelist_success(self):
        application = Application()

        # succeed no host value with defaults
        with ManagedServerLoop(application, host=None) as server:
            self.check_http_ok_socket_ok(server)

        # succeed no host value with port
        with ManagedServerLoop(application, port=8080, host=None) as server:
            self.check_http_ok_socket_ok(server)

        # succeed matching host value
        with ManagedServerLoop(application, port=8080,
                               host=["localhost:8080"]) as server:
            self.check_http_ok_socket_ok(server)

        # succeed matching host value one of multiple
        with ManagedServerLoop(application,
                               port=8080,
                               host=["bad_host", "localhost:8080"]) as server:
            self.check_http_ok_socket_ok(server)
    def _run_server(fnc_make_document,
                    iplot=True,
                    notebook_url="localhost:8889",
                    port=80,
                    ioloop=None):
        """Runs a Bokeh webserver application. Documents will be created using fnc_make_document"""
        handler = FunctionHandler(fnc_make_document)
        app = Application(handler)

        if iplot and 'ipykernel' in sys.modules:
            show(app, notebook_url=notebook_url)
        else:
            apps = {'/': app}

            print(f"Open your browser here: http://localhost:{port}")
            server = Server(apps, port=port, io_loop=ioloop)
            if ioloop is None:
                server.run_until_shutdown()
            else:
                server.start()
                ioloop.start()
예제 #17
0
    def finalize(self):
        """Finalize the link.

        :returns: status code of finalization
        :rtype: StatusCode
        """
        # --- any code to finalize the link follows here

        from bokeh.server.server import Server
        from bokeh.application import Application
        from bokeh.application.handlers.function import FunctionHandler

        apps = {"/": Application(FunctionHandler(self._doc_factory))}

        server = Server(applications=apps, port=5000)
        server.io_loop.add_callback(server.show, "/")

        try:
            server.io_loop.start()
        except KeyboardInterrupt:
            return StatusCode.Success
예제 #18
0
    def test_launch_server_with_complex_plot(self):
        dmap = DynamicMap(lambda x_range, y_range: Curve([]),
                          streams=[RangeXY()])
        overlay = dmap * HLine(0)
        static = Polygons([]) * Path([]) * Curve([])
        layout = overlay + static

        launched = []

        def modify_doc(doc):
            bokeh_renderer(layout, doc=doc)
            launched.append(True)
            server.stop()

        handler = FunctionHandler(modify_doc)
        app = Application(handler)
        server = Server({'/': app}, port=0)
        server.start()
        url = "http://localhost:" + str(server.port) + "/"
        pull_session(session_id='Test', url=url, io_loop=server.io_loop)
        self.assertTrue(len(launched) == 1)
예제 #19
0
def build_single_handler_application(path, argv=None):
    ''' Return a Bokeh application built using a single handler for a file
    or directory.

    Args:
        path (str) : path to a file or directory for creating a Bokeh
            application.
        argv (seq[str], optional) : command line arguments to pass to the
            application handler

    Returns:
        Application

    Raises:
        RuntimeError

    '''
    argv = argv or []
    path = os.path.abspath(path)
    if os.path.isdir(path):
        handler = DirectoryHandler(filename=path, argv=argv)
    else:
        if path.endswith(".ipynb"):
            handler = NotebookHandler(filename=path, argv=argv)
        elif path.endswith(".py"):
            if path.endswith("main.py"):
                warnings.warn(DIRSTYLE_MAIN_WARNING)
            handler = ScriptHandler(filename=path, argv=argv)
        else:
            raise ValueError(
                "Expected a '.py' script or '.ipynb' notebook, got: '%s'" %
                path)

    if handler.failed:
        raise RuntimeError("Error loading %s:\n\n%s\n%s " %
                           (path, handler.error, handler.error_detail))

    application = Application(handler)

    return application
    def test_io_push_to_server(self):
        from bokeh.io import output_server, push, curdoc, reset_output
        application = Application()
        with ManagedServerLoop(application) as server:
            reset_output()
            doc = curdoc()
            doc.clear()

            client_root = SomeModelInTestClientServer(foo=42)

            session_id = 'test_io_push_to_server'
            output_server(session_id=session_id,
                          url=("http://localhost:%d/" % server.port))

            doc.add_root(client_root)
            push(io_loop=server.io_loop)

            server_session = server.get_session('/', session_id)

            print(repr(server_session.document.roots))

            assert len(server_session.document.roots) == 1
            server_root = next(iter(server_session.document.roots))

            assert client_root.foo == 42
            assert server_root.foo == 42

            # Now modify the client document and push
            client_root.foo = 57
            push(io_loop=server.io_loop)
            server_root = next(iter(server_session.document.roots))
            assert server_root.foo == 57

            # Remove a root and push
            doc.remove_root(client_root)
            push(io_loop=server.io_loop)
            assert len(server_session.document.roots) == 0

            # Clean up global IO state
            reset_output()
예제 #21
0
    def _run_server(fnc_make_document,
                    notebook_url='localhost:8889',
                    iplot=True,
                    ioloop=None,
                    address='localhost',
                    port=8889,
                    autostart=False):
        '''
        Runs a Bokeh webserver application. Documents will be created using
        fnc_make_document
        '''

        handler = FunctionHandler(fnc_make_document)
        app = Application(handler)

        ipython = iplot and 'ipykernel' in sys.modules
        if ipython:
            try:
                get_ipython
            except:
                ipython = False

        if ipython:
            show(app, notebook_url=notebook_url)  # noqa
        else:
            apps = {'/': app}
            if autostart:
                print(f'Browser is launching at: http://localhost:{port}')
                view(f'http://localhost:{port}')
            else:
                print(f'Open browser at: http://localhost:{port}')
            server = Server(apps,
                            port=port,
                            allow_websocket_origin=[address],
                            io_loop=ioloop)
            if ioloop is None:
                server.run_until_shutdown()
            else:
                server.start()
                ioloop.start()
예제 #22
0
파일: base.py 프로젝트: phausamann/xrview
    def show(self,
             notebook_url=None,
             port=0,
             remake_layout=False,
             verbose=False):
        """ Show the app in a jupyter notebook.

        Parameters
        ----------
        notebook_url : str, optional
            The URL of the notebook. Will be determined automatically if not
            specified.

        port : int, default 0
            The port over which the app will be served. Chosen randomly if
            set to 0.

        remake_layout : bool, default False
            If True, call ``make_layout`` even when the layout has already
            been created. Note that any changes made by ``modify_figures``
            will be omitted.

        verbose : bool, default False
            If True, create the document once again outside of show_app in
            order to show errors.
        """
        if notebook_url is None:
            notebook_url = get_notebook_url()

        output_notebook(hide_banner=True)

        if verbose:
            self.make_layout()
            Application(FunctionHandler(self._modify_doc)).create_document()

        if self.layout is None or remake_layout or verbose:
            self.make_layout()

        show(self._modify_doc, None, notebook_url=notebook_url, port=port)
예제 #23
0
def test_websocket_origins(ManagedServerLoop, unused_tcp_port) -> None:
    application = Application()
    with ManagedServerLoop(application, port=unused_tcp_port) as server:
        assert server._tornado.websocket_origins == {
            "localhost:%s" % unused_tcp_port
        }

    # OK this is a bit of a confusing mess. The user-facing arg for server is
    # "allow_websocket_origin" which gets converted to "extra_websocket_origins"
    # for BokehTornado, which is exposed as a property "websocket_origins"...
    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo"]) as server:
        assert server._tornado.websocket_origins == {"foo:80"}

    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo:8080"]) as server:
        assert server._tornado.websocket_origins == {"foo:8080"}

    with ManagedServerLoop(application,
                           allow_websocket_origin=["foo:8080",
                                                   "bar"]) as server:
        assert server._tornado.websocket_origins == {"foo:8080", "bar:80"}
예제 #24
0
def figure(ip, directory, model) -> None:
    """Start bokeh server with the file passed."""
    # pylint: disable=import-outside-toplevel
    from bokeh.server.server import Server
    from bokeh.application import Application
    from bokeh.application.handlers.function import FunctionHandler

    from .figures.interactive_config import make_document

    # pylint: enable=import-outside-toplevel

    if isinstance(ip, str):
        ip = (ip,)
    if directory:
        directory = Path(directory)

    make_document = partial(make_document, directory=directory, models=model)

    apps = {"/": Application(FunctionHandler(make_document))}
    server = Server(apps, allow_websocket_origin=list(ip))
    server.run_until_shutdown()
    logger.info("Bokeh server terminated.")
예제 #25
0
def start_bokeh_server():
    """
    Create and start a bokeh server with a series of
    applications.
    """
    # Declare the absolute path to the demo application.
    demo_path = os.path.abspath("project/server/bokeh_apps/bokehDemo")

    # Declare the dictionary of applications to launch.
    apps = {
        '/bokehDemo': Application(DirectoryHandler(filename=demo_path)),
    }

    # Instantiate the Bokeh server.
    # Allow connections from the Flask application.
    server = Server(
        applications=apps,
        allow_websocket_origin=["127.0.0.1:5000"],
        # port=5006
    )
    server.start()
    server.io_loop.start()
예제 #26
0
    def test_host_whitelist_failure(self):
        application = Application()

        # failure bad host
        with ManagedServerLoop(application, host=["bad_host"]) as server:
            self.check_http_blocked_socket_blocked(server)

        with ManagedServerLoop(application, host=["bad_host:5006"]) as server:
            self.check_http_blocked_socket_blocked(server)

        # failure good host, bad port
        with ManagedServerLoop(application, host=["localhost:80"]) as server:
            self.check_http_blocked_socket_blocked(server)

        # failure good host, bad default port
        with ManagedServerLoop(application, host=["localhost"]) as server:
            self.check_http_blocked_socket_blocked(server)

        # failure with custom port
        with ManagedServerLoop(application, port=8080,
                               host=["localhost:8081"]) as server:
            self.check_http_blocked_socket_blocked(server)
def test__ioloop_not_forcibly_stopped():
    # Issue #5494
    application = Application()
    loop = IOLoop()
    loop.make_current()
    server = Server(application, ioloop=loop)
    server.start()
    result = []

    def f():
        server.unlisten()
        server.stop()
        # If server.stop() were to stop the Tornado IO loop,
        # g() wouldn't be called and `result` would remain empty.
        loop.add_timeout(timedelta(seconds=0.01), g)

    def g():
        result.append(None)
        loop.stop()

    loop.add_callback(f)
    loop.start()
    assert result == [None]
예제 #28
0
    def add_app(self, uri, makefn):
        """
        Adds an app to the server

        the app is a standard :class:`bokeh.application.application.Application` that is created by passing either
        an arbitrary makefunction (see :class:`bokeh.application.handlers.function.FunctionHandler`), or a :class:`BokehDash`
        object (which implements the same make function as the make_doc method).

        Args:
            uri:    The URI to serve the app on (e.g. '/dash', or '/' or ...)
            makefn: The app to add (can be either a BokehDash app, or an arbitrary user-define app maker function.
                    If you are implementing arbitrary app-maker ensure it follows the format required by
                     :class:`bokeh.application.handlers.function.FunctionHandler`

        Returns:

        """
        if isinstance(makefn, BokehDash):
            makefn = makefn.make_doc
        elif not callable(makefn):
            raise Error("App must either be of type BokehDash (i.e. with a make_doc method), or a callable")

        self._apps[uri] = Application(FunctionHandler(makefn))
예제 #29
0
def run_server(
    cell_stack,
    input_data,
    port=5000,
    markers=None,
    default_cell_marker=None,
    server_kwargs={},
):
    print(server_kwargs)
    apps = {
        "/":
        Application(
            FunctionHandler(
                partial(
                    prepare_server,
                    cell_stack=cell_stack,
                    input_data=input_data,
                    cell_markers=markers,
                    default_cell_marker=default_cell_marker,
                )))
    }
    server = Server(apps, port=port, **server_kwargs)
    server.run_until_shutdown()
예제 #30
0
def test_default_resources(ManagedServerLoop) -> None:
    application = Application()
    with ManagedServerLoop(application) as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == ""
        assert r.path_versioner == StaticHandler.append_version

    with ManagedServerLoop(application, prefix="/foo/") as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == "/foo/"
        assert r.path_versioner == StaticHandler.append_version

    with ManagedServerLoop(application, prefix="foo/") as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == "/foo/"
        assert r.path_versioner == StaticHandler.append_version

    with ManagedServerLoop(application, prefix="foo") as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == "/foo/"
        assert r.path_versioner == StaticHandler.append_version

    with ManagedServerLoop(application, prefix="/foo") as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == "/foo/"
        assert r.path_versioner == StaticHandler.append_version

    with ManagedServerLoop(application, prefix="/foo/bar") as server:
        r = server._tornado.resources()
        assert r.mode == "server"
        assert r.root_url == "/foo/bar/"
        assert r.path_versioner == StaticHandler.append_version