Skip to content

Commit

Permalink
Migrate streamlit hello to MPAv2 (streamlit#8806)
Browse files Browse the repository at this point in the history
## Describe your changes

This PR converts `streamlit hello` to use Multipage Apps v2. We overall
kept the change very simple to mimic the same look and feel. We can
adjust it accordingly.

A minor change to improve types is here when we cannot find the context.
This should be impossible in a Streamlit app, but for bare execution and
to make the return type to ALWAYS be a Page, we return the default page
to run.

## Testing Plan

- Same E2E and unit tests should pass

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
  • Loading branch information
kmcgrady authored Jun 3, 2024
1 parent 143c707 commit eca9170
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 51 deletions.
16 changes: 4 additions & 12 deletions e2e_playwright/hello_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,10 @@

import numpy as np

from streamlit import source_util
from streamlit.hello import Hello
from streamlit import runtime
from streamlit.hello import streamlit_app

# Set random seed to always get the same results in the plotting demo
np.random.seed(0)

# This is a trick to setup the MPA hello app programmatically
source_util._cached_pages = None
source_util._cached_pages = source_util.get_pages(Hello.__file__)
source_util._on_pages_changed.send()

# TODO(lukasmasuch): Once we migrate the hello app to the new programmatic
# MPA API, we can remove this workaround.

Hello.run()
if runtime.exists():
streamlit_app.run()
13 changes: 8 additions & 5 deletions lib/streamlit/commands/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def navigation(
pages: list[StreamlitPage] | dict[SectionHeader, list[StreamlitPage]],
*,
position: Literal["sidebar", "hidden"] = "sidebar",
) -> StreamlitPage | None:
) -> StreamlitPage:
"""
Configure the available pages in a multipage app.
Expand Down Expand Up @@ -91,10 +91,6 @@ def navigation(
>>> st.title("My Awesome App")
>>> pg.run()
"""
ctx = get_script_run_ctx()
if not ctx:
return None

nav_sections = {"": pages} if isinstance(pages, list) else pages
page_list = pages_from_nav_sections(nav_sections)

Expand Down Expand Up @@ -162,6 +158,13 @@ def navigation(
p.section_header = section_header
p.url_pathname = page.url_path

ctx = get_script_run_ctx()
if not ctx:
# This should never run in Streamlit, but we want to make sure that
# the function always returns a page
default_page._can_be_called = True
return default_page

# Inform our page manager about the set of pages we have
ctx.pages_manager.set_pages(pagehash_to_pageinfo)
found_page = ctx.pages_manager.get_page_script(
Expand Down
File renamed without changes.
File renamed without changes.
50 changes: 22 additions & 28 deletions lib/streamlit/hello/Hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,29 @@

import streamlit as st

st.set_page_config(
page_title="Hello",
page_icon="👋",
)

def run():
st.set_page_config(
page_title="Hello",
page_icon="👋",
)
st.write("# Welcome to Streamlit! 👋")

st.write("# Welcome to Streamlit! 👋")
st.sidebar.success("Select a demo above.")

st.sidebar.success("Select a demo above.")

st.markdown(
"""
Streamlit is an open-source app framework built specifically for
Machine Learning and Data Science projects.
**👈 Select a demo from the sidebar** to see some examples
of what Streamlit can do!
### Want to learn more?
- Check out [streamlit.io](https://streamlit.io)
- Jump into our [documentation](https://docs.streamlit.io)
- Ask a question in our [community
forums](https://discuss.streamlit.io)
### See more complex demos
- Use a neural net to [analyze the Udacity Self-driving Car Image
Dataset](https://github.com/streamlit/demo-self-driving)
- Explore a [New York City rideshare dataset](https://github.com/streamlit/demo-uber-nyc-pickups)
st.markdown(
"""
)


if __name__ == "__main__":
run()
Streamlit is an open-source app framework built specifically for
Machine Learning and Data Science projects.
**👈 Select a demo from the sidebar** to see some examples
of what Streamlit can do!
### Want to learn more?
- Check out [streamlit.io](https://streamlit.io)
- Jump into our [documentation](https://docs.streamlit.io)
- Ask a question in our [community
forums](https://discuss.streamlit.io)
### See more complex demos
- Use a neural net to [analyze the Udacity Self-driving Car Image
Dataset](https://github.com/streamlit/demo-self-driving)
- Explore a [New York City rideshare dataset](https://github.com/streamlit/demo-uber-nyc-pickups)
"""
)
File renamed without changes.
File renamed without changes.
37 changes: 37 additions & 0 deletions lib/streamlit/hello/streamlit_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path

import streamlit as st

dir_path = Path(__file__).parent


def run():
page = st.navigation(
[
st.Page(dir_path / "Hello.py"),
st.Page(dir_path / "Animation_Demo.py"),
st.Page(dir_path / "Plotting_Demo.py"),
st.Page(dir_path / "Mapping_Demo.py"),
st.Page(dir_path / "Dataframe_Demo.py"),
]
)

page.run()


if __name__ == "__main__":
run()
4 changes: 2 additions & 2 deletions lib/streamlit/web/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ def main_docs():
@configurator_options
def main_hello(**kwargs):
"""Runs the Hello World script."""
from streamlit.hello import Hello
from streamlit.hello import streamlit_app

bootstrap.load_config_options(flag_options=kwargs)
filename = Hello.__file__
filename = streamlit_app.__file__
_main_run(filename, flag_options=kwargs)


Expand Down
4 changes: 2 additions & 2 deletions lib/streamlit/web/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,9 @@ def browser_is_connected(self) -> bool:

@property
def is_running_hello(self) -> bool:
from streamlit.hello import Hello
from streamlit.hello import streamlit_app

return self._main_script_path == Hello.__file__
return self._main_script_path == streamlit_app.__file__

def stop(self) -> None:
cli_util.print_to_cli(" Stopping...", fg="blue")
Expand Down
4 changes: 2 additions & 2 deletions lib/tests/streamlit/web/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,14 +319,14 @@ def test_docs_command(self):

def test_hello_command(self):
"""Tests the hello command runs the hello script in streamlit"""
from streamlit.hello import Hello
from streamlit.hello import streamlit_app

with patch("streamlit.web.cli._main_run") as mock_main_run:
self.runner.invoke(cli, ["hello"])

mock_main_run.assert_called_once()
positional_args = mock_main_run.call_args[0]
self.assertEqual(positional_args[0], Hello.__file__)
self.assertEqual(positional_args[0], streamlit_app.__file__)

@patch("streamlit.logger.get_logger")
def test_hello_command_with_logs(self, get_logger):
Expand Down

0 comments on commit eca9170

Please sign in to comment.