From 946cd38b288ab0b2ddf219f9241113b3570b0133 Mon Sep 17 00:00:00 2001 From: maximelqt Date: Mon, 18 May 2020 21:59:25 +0200 Subject: [PATCH 1/4] Customize title for multi apps --- panel/io/server.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/panel/io/server.py b/panel/io/server.py index 3dd52bd6b6..03020a7da4 100644 --- a/panel/io/server.py +++ b/panel/io/server.py @@ -130,8 +130,9 @@ def serve(panels, port=0, websocket_origin=None, loop=None, show=True, Whether to open the server in a new browser tab on start start : boolean(optional, default=False) Whether to start the Server - title: str (optional, default=None) - An HTML title for the application + title: str or {str: str} (optional, default=None) + An HTML title for the application or a dictionnary mapping + from the URL slug to a customized title verbose: boolean (optional, default=True) Whether to print the address and port location : boolean or panel.io.location.Location @@ -188,8 +189,9 @@ def get_server(panel, port=0, websocket_origin=None, loop=None, Whether to open the server in a new browser tab on start start : boolean(optional, default=False) Whether to start the Server - title: str (optional, default=None) - An HTML title for the application + title: str or {str: str} (optional, default=None) + An HTML title for the application or a dictionnary 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 @@ -210,6 +212,16 @@ def get_server(panel, port=0, websocket_origin=None, loop=None, 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"dictionnary 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 @@ -222,7 +234,7 @@ def get_server(panel, port=0, websocket_origin=None, loop=None, extra_patterns.append(('^'+slug+'.*', ProxyFallbackHandler, dict(fallback=wsgi, proxy=slug))) continue - apps[slug] = partial(_eval_panel, app, server_id, title, location) + apps[slug] = partial(_eval_panel, app, server_id, title_, location) else: apps = {'/': partial(_eval_panel, panel, server_id, title, location)} From 0fd1c63ed54d94b549e44592370791d83edb221e Mon Sep 17 00:00:00 2001 From: maximelqt Date: Mon, 18 May 2020 22:06:10 +0200 Subject: [PATCH 2/4] Test the title kwarg of pn.serve --- panel/tests/conftest.py | 31 +++++++++++++++++++++++++++++++ panel/tests/test_server.py | 14 ++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/panel/tests/conftest.py b/panel/tests/conftest.py index 4054397840..b875ab17ed 100644 --- a/panel/tests/conftest.py +++ b/panel/tests/conftest.py @@ -17,6 +17,7 @@ from panel.pane import HTML, Markdown from panel.io import state +from panel import serve @pytest.fixture @@ -117,6 +118,36 @@ def markdown_server_session(): pass # tests may already close this +@pytest.fixture +def multiple_apps_server_sessions(): + """Serve multiple apps and yield a factory to allow + parameterizing the slugs and the titles.""" + servers = [] + def create_sessions(slugs, titles): + app1_slug, app2_slug = slugs + apps = { + app1_slug: Markdown('First app'), + app2_slug: Markdown('Second app') + } + server = serve(apps, port=5008, title=titles, show=False, start=False) + servers.append(server) + session1 = pull_session( + url=f"http://localhost:{server.port:d}/app1", + io_loop=server.io_loop + ) + session2 = pull_session( + url=f"http://localhost:{server.port:d}/app2", + io_loop=server.io_loop + ) + return session1, session2 + yield create_sessions + for server in servers: + try: + server.stop() + except AssertionError: + continue # tests may already close this + + @contextmanager def set_env_var(env_var, value): old_value = os.environ.get(env_var) diff --git a/panel/tests/test_server.py b/panel/tests/test_server.py index 89b4a32e65..45444cfa54 100644 --- a/panel/tests/test_server.py +++ b/panel/tests/test_server.py @@ -1,3 +1,5 @@ +import pytest + from panel.models import HTML as BkHTML from panel.io import state @@ -43,3 +45,15 @@ def test_kill_all_servers(html_server_session, markdown_server_session): state.kill_all_servers() assert server_1._stopped assert server_2._stopped + +def test_multiple_titles(multiple_apps_server_sessions): + """Serve multiple apps with a title per app.""" + session1, session2 = multiple_apps_server_sessions( + slugs=('app1', 'app2'), titles={'app1': 'APP1', 'app2': 'APP2'}) + assert session1.document.title == 'APP1' + assert session2.document.title == 'APP2' + + # Slug names and title keys should match + with pytest.raises(KeyError): + session1, session2 = multiple_apps_server_sessions( + slugs=('app1', 'app2'), titles={'badkey': 'APP1', 'app2': 'APP2'}) From be6ed910ec1c724fea4168e695e66b59a4eab217 Mon Sep 17 00:00:00 2001 From: maximelqt Date: Mon, 18 May 2020 22:06:31 +0200 Subject: [PATCH 3/4] Update the deploy docs with the kwarg title of pn.serve --- examples/user_guide/Deploy_and_Export.ipynb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/user_guide/Deploy_and_Export.ipynb b/examples/user_guide/Deploy_and_Export.ipynb index 2af6a5828a..b37071c1a0 100644 --- a/examples/user_guide/Deploy_and_Export.ipynb +++ b/examples/user_guide/Deploy_and_Export.ipynb @@ -229,6 +229,14 @@ "pn.serve({'markdown': '# This is a Panel app', 'json': pn.pane.JSON({'abc': 123})})\n", "```\n", "\n", + "You can customize the HTML title of each application by supplying a dictionnary where the keys represent the URL slugs and the values represent the titles, e.g.:\n", + "\n", + "```python\n", + "pn.serve(\n", + " {'markdown': '# This is a Panel app', 'json': pn.pane.JSON({'abc': 123})},\n", + " title={'markdown': 'A Markdown App', 'json': 'A JSON App'}\n", + ")\n", + "\n", "The ``pn.serve`` function accepts the same arguments as the `show` method.\n", "\n", "\n", From 22c3107a65e56ebac6e2381f77a0f8d34dd29e2a Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 19 May 2020 12:37:37 +0200 Subject: [PATCH 4/4] Apply suggestions from code review --- examples/user_guide/Deploy_and_Export.ipynb | 2 +- panel/io/server.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/user_guide/Deploy_and_Export.ipynb b/examples/user_guide/Deploy_and_Export.ipynb index b37071c1a0..0af762a439 100644 --- a/examples/user_guide/Deploy_and_Export.ipynb +++ b/examples/user_guide/Deploy_and_Export.ipynb @@ -229,7 +229,7 @@ "pn.serve({'markdown': '# This is a Panel app', 'json': pn.pane.JSON({'abc': 123})})\n", "```\n", "\n", - "You can customize the HTML title of each application by supplying a dictionnary where the keys represent the URL slugs and the values represent the titles, e.g.:\n", + "You can customize the HTML title of each application by supplying a dictionary where the keys represent the URL slugs and the values represent the titles, e.g.:\n", "\n", "```python\n", "pn.serve(\n", diff --git a/panel/io/server.py b/panel/io/server.py index 03020a7da4..5f16f33f6c 100644 --- a/panel/io/server.py +++ b/panel/io/server.py @@ -131,7 +131,7 @@ def serve(panels, port=0, websocket_origin=None, loop=None, show=True, 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 dictionnary mapping + An HTML title for the application or a dictionary mapping from the URL slug to a customized title verbose: boolean (optional, default=True) Whether to print the address and port @@ -190,7 +190,7 @@ def get_server(panel, port=0, websocket_origin=None, loop=None, 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 dictionnary mapping + 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 @@ -218,7 +218,7 @@ def get_server(panel, port=0, websocket_origin=None, loop=None, except KeyError: raise KeyError( "Keys of the title dictionnary and of the apps " - f"dictionnary must match. No {slug} key found in the " + f"dictionary must match. No {slug} key found in the " "title dictionnary.") else: title_ = title