Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamlit migration Guide #5027

Merged
merged 55 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b17c08f
Implement support for reactive generator functions
philippjfr Jun 2, 2023
2074140
Add synchronization primitives for async generators
philippjfr Jun 3, 2023
af7c84d
Ensure safe cleanup
philippjfr Jun 3, 2023
e139537
Various fixes and tests
philippjfr Jun 3, 2023
900d440
Add pytest-asyncio to test dependencies
philippjfr Jun 3, 2023
e03342a
Support for binding refs
philippjfr Jun 3, 2023
7f3148d
add beginning of streamlit migration guide
Jun 3, 2023
67cf0dd
add more migration from streamlit info
Jun 4, 2023
8096a5f
more streamlit migration
Jun 5, 2023
7b79e44
improve pn.panel docs
Jun 5, 2023
8daaab9
merge with main and improve streamlit
Jun 5, 2023
4fee242
commit latest changes for streamlit
Jun 5, 2023
19ebb17
fix merge issues
Jun 5, 2023
d87ffc4
fix spell issue
Jun 5, 2023
faf329a
Merge branch 'main' into enhancement/streamlit-migration-guide
Jun 5, 2023
6bdb0c5
Add streamlit content
Jun 5, 2023
2cf4875
add some images
Jun 5, 2023
41bedca
add more images to streamlit migration
Jun 5, 2023
c99f236
update streamlit template section
Jun 5, 2023
20c7486
fix minor streamlit issues
Jun 5, 2023
133715a
fix minor issues
Jun 5, 2023
3833a61
add one word
Jun 5, 2023
80c0b38
fix minor streamlit migration issues
Jun 5, 2023
5f41516
Update doc/how_to/interactivity/bind_generators.md
MarcSkovMadsen Jun 12, 2023
122d1d7
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
21d0b37
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
baa8168
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
72b2c43
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
2139e1c
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
88d20bc
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 12, 2023
218d5d8
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 13, 2023
89d71bf
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 13, 2023
3d699b0
Update doc/how_to/migrate/from_streamlit.md
MarcSkovMadsen Jun 13, 2023
4399b07
carve out streamlit comparison
Jun 13, 2023
431dbc5
make description more specific
Jun 13, 2023
447063b
make description more explicit
Jun 13, 2023
1ae3f3e
use andrews suggestions
Jun 14, 2023
112cea1
Apply suggestions from code review
MarcSkovMadsen Jun 14, 2023
fbbbe88
Apply suggestions from code review
MarcSkovMadsen Jun 15, 2023
2605866
Replace all assets
philippjfr Jun 15, 2023
4027138
Add image assets
philippjfr Jun 15, 2023
0c78f51
simplify example
Jun 16, 2023
4258df5
Merge branch 'enhancement/streamlit-migration-guide' of https://githu…
Jun 16, 2023
216167a
correct caching example
Jun 16, 2023
894c279
Apply suggestions from code review
MarcSkovMadsen Jun 16, 2023
16a73b1
Merge branch 'enhancement/streamlit-migration-guide' of https://githu…
Jun 16, 2023
cc5d8a7
show activity example
Jun 16, 2023
f6c74b7
add improvements from teaching ChatGPT to migrate
Jun 16, 2023
fec0a64
refactor based on ChatGPT experience
Jun 17, 2023
45b52b3
make more precise
Jun 24, 2023
cf3a228
add session state
Jun 24, 2023
c420dc6
Merge remote-tracking branch 'origin/main' into enhancement/streamlit…
philippjfr Jun 30, 2023
19c079b
Various small updates
philippjfr Jun 30, 2023
0b422dc
migrate streamlit to individual guides
MarcSkovMadsen Jul 2, 2023
7ffd59d
fix minor issues
MarcSkovMadsen Jul 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/_static/images/panel_dynamic_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/panel_hello_world.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/panel_layout_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/panel_markdown_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/panel_mpl_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/panel_widgets_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/slides_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/slides_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/streamlit_dymamic_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/streamlit_hello_world.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/streamlit_layout_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/streamlit_mpl_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/_static/images/streamlit_widgets_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion doc/how_to/caching/manual.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Manually Cache

This guide addresses how to manually cache data and objects on global object - `pn.state.cache`.
This guide addresses how to cache data and objects globally across user sessions - `pn.state.cache`.
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved

---

Expand Down
20 changes: 20 additions & 0 deletions doc/how_to/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,25 @@ How to run Panel applications entirely in the browser using WebAssembly (Wasm),

::::

## Migrate to Panel
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved

::::{grid} 1 2 2 3
:gutter: 1 1 1 2

:::{grid-item-card} Migrate from Streamlit
:link: migrate/streamlit/index
:link-type: doc

```{image} https://assets.holoviz.org/panel/background/comparisons/streamlit_logo.png
:width: 125px
:align: center
:name: Streamlit
```

:::

::::

```{toctree}
:titlesonly:
:hidden:
Expand All @@ -237,4 +256,5 @@ manage_session_tasks
test_and_debug
prepare_to_share
share_your_work
migrate_to_panel
```
8 changes: 6 additions & 2 deletions doc/how_to/interactivity/bind_generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import panel as pn
pn.extension()
```

Let us say we have some action that is triggered by a widget, such as a button, and while we are computing the results we want to provide feedback to the user. Using imperative programming this involves writing callbacks that update the current state of our components. This is complex and really we prefer to write reactive components. This is where reactive generators come in.
Let us say we have some action that is triggered by a widget, such as a button, and while we are computing the results we want to provide feedback to the user. Using imperative programming this involves writing callbacks that update the current state of our components. This is complex and really we prefer to write reactive components. This is where *generator functions* come in.

:::{important}
MarcSkovMadsen marked this conversation as resolved.
Show resolved Hide resolved
A *generator* function is a function that use `yield` to *return* results as they are produced during the execution. It is not allowed to `return` anything, but can use `return` to *break* the execution. For an introduction to *generator functions* check out [Real Python | Introduction to generator functions](https://realpython.com/introduction-to-python-generators/).
:::

In the example below we add a `Button` to trigger some calculation. Initially the calculation hasn't yet run, so we check the value provided by the `Button` indicating whether a calculation has been triggered and while it is `False` we `yield` some text and `return`. However, when the `Button` is clicked the function is called again with `run=True` and we kick off some calculation. As this calculation progresses we can `yield` updates and then once the calculation is successful we `yield` again with the final result:

Expand All @@ -33,7 +37,7 @@ def runner(run):
pn.Row(run, pn.bind(runner, run))
```

This provides a powerful mechanism for providing incrememental updates as we load some data, perform some data processing processing.
This provides a powerful mechanism for providing incrememental updates as we load some data, perform some data processing, etc.

This can also be combined with asynchronous processing, e.g. to dynamically stream in new data as it arrives:

Expand Down
71 changes: 71 additions & 0 deletions doc/how_to/migrate/streamlit/activity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Show Activity

Panel supports many ways of indicating activity

- Indicators. See the [Indicators Section](../../../reference/index.md#indicators) of the [Component Gallery](../../../reference/index.md).
- `disabled`/ `loading` parameters on Panel components
- `loading_indicator` parameter for `pn.panel` or `pn.config`. If `True` a loading indicator will be shown on your *bound functions* when they are re-run.

## Example

The example below showcases some of the ways Panel can show activity.

```python
import panel as pn

pn.extension(sizing_mode="stretch_width", template="bootstrap")

SPIN_CSS = """
@keyframes icon-rotation {
from {transform: rotate(0deg);} to {transform: rotate(359deg);}
}
.bk-TablerIcon {animation: icon-rotation 2s infinite linear;}
"""

pn.Row(
pn.Column(
"## Loading Spinner",
pn.Column(
pn.indicators.LoadingSpinner(value=False, height=25, width=25),
pn.indicators.LoadingSpinner(
value=True, height=25, width=25, color="secondary"
),
),
),
pn.Column(
"## Progress",
pn.Column(
pn.indicators.Progress(
name="Progress", value=20, width=150, bar_color="secondary"
),
pn.indicators.Progress(
name="Progress", active=True, width=150, bar_color="secondary"
),
),
),
pn.Column(
"## Disabled",
pn.Column(
pn.widgets.Button(name="Loading", icon="progress", disabled=True),
pn.widgets.Button(
name="Loading", icon="progress", disabled=True, stylesheets=[SPIN_CSS]
),
),
),
pn.Column(
"## Loading",
pn.Column(
pn.widgets.Button(name="Loading", loading=True, button_type="primary"),
pn.WidgetBox(
pn.widgets.Checkbox(name="Checked", value=True),
pn.widgets.Button(name="Submit", button_type="primary"),
loading=True, margin=(10,10),
),
),
),
).servable()
```

![Show Activity](https://user-images.githubusercontent.com/42288570/246325570-11484dd6-4523-401f-b709-6c0cc7996410.gif)

To learn more about migrating activity indicators check out the [Migrate Streamlit Interactivity Guide](interactivity.md).
108 changes: 108 additions & 0 deletions doc/how_to/migrate/streamlit/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Improve the performance with Caching

One of the key concepts in Streamlit is *caching*.

In Streamlit

- your script is run once when a user visits the page.
- your script is rerun *top to bottom* on user interactions.

Thus with Streamlit you *must use* caching to make the user experience nice and fast.

In Panel

- your script is run once when a user visits the page.
- only *specific, bound functions* are rerun on user interactions.

Thus with Panel you *may use* caching to to make the user experience nice and fast.

In Panel you use `pn.cache` to speed up your apps. Check out the [Cache How-To Guides](../caching/index.md) for more details.

---

## Migration Steps

To migrate

- replace `st.cache_data` and `st.cache_resource` with `pn.cache` on long running
- functions that are run when your page loads
- *bound functions*

## Example

### Cache Example

#### Streamlit Cache Example

```python
from time import sleep

import numpy as np
import streamlit as st
from matplotlib.figure import Figure

@st.cache_data
def get_data():
print("get_data func")
sleep(1.0)
return np.random.normal(1, 1, size=100)

@st.cache_data(hash_funcs={Figure: lambda _: None})
def plot(data, bins):
print("plot func", bins)
sleep(2)
fig = Figure(figsize=(8,4))
ax = fig.subplots()
ax.hist(data, bins=bins)
return fig

data = get_data()
bins = st.slider(value=20, min_value=10, max_value=30, step=1, label="Bins")
st.pyplot(plot(data, bins))
```

I've added `sleep` statements to make the functions more *expensive*.

#### Panel Cache Example

```python
from time import sleep

import numpy as np
import panel as pn
from matplotlib.figure import Figure

@pn.cache
def get_data():
print("get_data func")
sleep(1.0)
return np.random.normal(1, 1, size=100)

@pn.cache
def plot(data, bins):
print("plot func", bins)
sleep(2)
fig = Figure(figsize=(8,4))
ax = fig.subplots()
ax.hist(data, bins=bins)
return fig

pn.extension(sizing_mode="stretch_width", template="bootstrap")

data = get_data()
bins = pn.widgets.IntSlider(value=20, start=10, end=30, step=1)
bplot = pn.bind(plot, data, bins)
pn.Column(bins, pn.panel(bplot, loading_indicator=True)).servable()
```

![Panel Cache Example](https://assets.holoviz.org/panel/gifs/panel_cache_example.gif)

You can also use `pn.cache` as an function. I.e. as

```python
plot = pn.cache(plot)
```

Using `pn.cache` as a function can help you keep your business logic
(`data` and `plot` function) and your caching logic (when and how to apply caching) separate. This
can help you reusable and maintainable code.
62 changes: 62 additions & 0 deletions doc/how_to/migrate/streamlit/get_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Get Started migrating from Streamlit to Panel

This guide addresses the basics of migrating from Streamlit to Panel.

---

## Migration Steps

You should replace:

- `import streamlit as st` with `import panel as pn` and
- `st.write` with `pn.panel`.

You will have to:

- add `pn.extension` to configure your Panel application via optional arguments like `sizing_mode` and `template`.
- add `.servable` to the Panel objects you want to include in your apps *template* when served as
a web app.

For production you will also have to migrate some of your app configuration to `panel serve` [command line options](../server/commandline.md) or environment variables.

You **won't** have to provide your email or opt out of telemetry data collection. We have never collected or had plans to collect telemetry data from our users apps.

## Examples

### Hello World

Lets show how to convert a *Hello World* application.

### Streamlit Hello World Example

```python
import streamlit as st

st.write("Hello World")
```

You *run* and *show* the app with *autoreload* via

```bash
streamlit run app.py
```

![Streamlit Hello World Example](../../../_static/images/streamlit_hello_world.png)

### Panel Hello World Example

```python
import panel as pn

pn.extension(sizing_mode="stretch_width", template="bootstrap")

pn.panel("Hello World").servable()
```

You *serve* and *show* (i.e. open) the app in your browser with *autoreload* via

```bash
panel serve app.py --autoreload --show
```

![Panel Hello World Example](../../../_static/images/panel_hello_world.png)
Loading
Loading