Skip to content

Commit

Permalink
Release 1.33.0 (#1026)
Browse files Browse the repository at this point in the history
* Update generate.py

* Update AppTest with new method

* Add st.html

* Add st.fragment

* Update streamlit.json

* Update streamlit.json

* Remove unused name override for components

* Fragments overview first draft

* Use example.com for iframe example

* Fix file extension

* Update fragments overview

* Typos

* Fragments are easy to understand

* Semantic function names

* Update changelog.md

* Add AreaChartColumn

* Update What's new

* Update api-cheat-sheet.md
  • Loading branch information
sfc-gh-dmatthews authored Apr 5, 2024
1 parent 9165481 commit 5e9d0c8
Show file tree
Hide file tree
Showing 22 changed files with 9,591 additions and 64 deletions.
7 changes: 1 addition & 6 deletions components/blocks/autofunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,9 @@ const Autofunction = ({
if (hideHeader !== undefined && hideHeader) {
header = "";
} else {
const functionName = functionObject.signature
const name = functionObject.signature
? `${functionObject.signature}`.split("(")[0].replace("streamlit", "st")
: "";
const name =
String(functionObject.name).startsWith("html") ||
String(functionObject.name).startsWith("iframe")
? `st.components.v1.${functionObject.name}`
: functionName;
header = (
<div className={styles.HeaderContainer}>
<div
Expand Down
34 changes: 32 additions & 2 deletions content/develop/api-reference/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,7 @@ st.page_link("pages/profile.py", label="My profile")
<br />

<TileContainer>
<RefCard href="/develop/api-reference/execution-flow/st.form">
<RefCard href="/develop/api-reference/execution-flow/st.form" size="half">

<h4>Forms</h4>

Expand All @@ -1863,6 +1863,20 @@ with st.form(key='my_form'):
st.form_submit_button("Sign up")
```

</RefCard>
<RefCard href="/develop/api-reference/execution-flow/st.fragment" size="half">

<h4>Partial reruns</h4>

Define a fragment to rerun independently from the rest of the script.

```python
@st.experimental_fragment(run_every="10s")
def fragment():
df = get_data()
st.line_chart(df)
```

</RefCard>
<RefCard href="/develop/api-reference/execution-flow/st.rerun">

Expand Down Expand Up @@ -2220,7 +2234,7 @@ st.components.v1.iframe(

</TileContainer>

### Utilities and data
### Utilities and user info

<br />

Expand Down Expand Up @@ -2250,6 +2264,22 @@ st.help(st.write)
st.help(pd.DataFrame)
```

</RefCard>
<RefCard href="/develop/api-reference/utilities/st.html">

<h4>Render HTML</h4>

Renders HTML strings to your app.

```python
css = """
<style>
p { color: red; }
</style>
"""
st.html(css)
```

</RefCard>
</TileContainer>

Expand Down
15 changes: 15 additions & 0 deletions content/develop/api-reference/control-flow/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ By default, Streamlit apps execute the script entirely, but we allow some functi

<TileContainer>

<RefCard href="/develop/api-reference/execution-flow/st.fragment">

<h4>Partial reruns</h4>

Define a fragment to rerun independently from the rest of the script.

```python
@st.experimental_fragment(run_every="10s")
def fragment():
df = get_data()
st.line_chart(df)
```

</RefCard>

<RefCard href="/develop/api-reference/execution-flow/st.rerun">

<h4>Rerun script</h4>
Expand Down
7 changes: 7 additions & 0 deletions content/develop/api-reference/control-flow/fragment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: st.fragment
slug: /develop/api-reference/execution-flow/st.fragment
description: st.fragment is a decorator that allows a function to rerun independantly
---

<Autofunction function="streamlit.experimental_fragment" />
2 changes: 1 addition & 1 deletion content/develop/api-reference/custom-components/iframe.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ import streamlit as st
import streamlit.components.v1 as components

# embed streamlit docs in a streamlit app
components.iframe("https://docs.streamlit.io/en/latest")
components.iframe("https://example.com", height=500)
```
13 changes: 13 additions & 0 deletions content/develop/api-reference/data/column_config/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,19 @@ ImageColumn("Preview Image", help="The preview screenshots")

</RefCard>

<RefCard href="/develop/api-reference/data/st.column_config/st.column_config.areachartcolumn">
<Image pure alt="screenshot" src="/images/api/column_config.areachartcolumn.jpg" />

<h4>Area chart column</h4>

Configure an area chart column.

```python
AreaChartColumn("Sales (last 6 months)" y_min=0, y_max=100)
```

</RefCard>

<RefCard href="/develop/api-reference/data/st.column_config/st.column_config.linechartcolumn">
<Image pure alt="screenshot" src="/images/api/column_config.linechartcolumn.jpg" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: st.column_config.AreaChartColumn
slug: /develop/api-reference/data/st.column_config/st.column_config.areachartcolumn
---

<Autofunction function="streamlit.column_config.AreaChartColumn" />
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ slug: /develop/api-reference/app-testing/st.testing.v1.apptest

<Autofunction function="AppTest.run" />

<Autofunction function="AppTest.switch_page" />

# Get AppTest script elements

The main value of `AppTest` is providing an API to programmatically inspect and interact with the elements and widgets produced by a running Streamlit app. Using the `AppTest.<element type>` properties or `AppTest.get()` method returns a collection of all the elements or widgets of the specified type that would have been displayed by running the app.
Expand Down
20 changes: 18 additions & 2 deletions content/develop/api-reference/utilities/_index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Placeholders, help, and options
title: Utilities and user info
slug: /develop/api-reference/utilities
---

# Placeholders, help, and options
# Utilities and user info

There are a handful of methods that allow you to create placeholders in your
app, provide help using doc strings, get and modify configuration options and query parameters.
Expand Down Expand Up @@ -34,5 +34,21 @@ st.help(st.write)
st.help(pd.DataFrame)
```

</RefCard>
<RefCard href="/develop/api-reference/utilities/st.html" size="half">

<h4>Render HTML</h4>

Renders HTML strings to your app.

```python
css = """
<style>
p { color: red; }
</style>
"""
st.html(css)
```

</RefCard>
</TileContainer>
7 changes: 7 additions & 0 deletions content/develop/api-reference/utilities/html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: st.html
slug: /develop/api-reference/utilities/st.html
description: st.html renders arbitrary HTML strings to your app
---

<Autofunction function="streamlit.html" />
143 changes: 143 additions & 0 deletions content/develop/concepts/architecture/fragments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
title: Working with fragments and partial reruns
slug: /develop/concepts/architecture/fragments
---

# Working with fragments and partial reruns

Reruns are a central part of every Streamlit app. When users interact with widgets, your script reruns from top to bottom and your app's frontend is updated. Streamlit provides several features to help you develop your app within this execution model. Streamlit version 1.33.0 introduced fragments to allow rerunning a portion or your code instead of your full script. As your app grows larger and more complex, these partial reruns help your app be efficient and performant. Fragments give you finer, easy-to-understand control over your app's execution flow.

Before you read about fragments, we recommend having a basic understanding of [caching](/develop/concepts/architecture/caching), [Session State](/concepts/architecture/session-state), and [forms](/develop/concepts/architecture/forms).

## Use cases for fragments

Fragments are versatile and applicable to a wide variety of circumstances. Here are just a few, common scenarios where fragments are useful:

- Your app has multiple visualizations and each one takes time to load, but you have a filter input that only updates one of them.
- You have a dynamic form that doesn't need to update the rest of your app (until the form is complete).
- You want to automatically update a single component or group of components to stream data.

## Defining and calling a fragment

Streamlit provides a decorator ([`st.experimental_fragment`](/develop/api-reference/execution-flow/st.fragment)) to turn any function into a fragment function. When you call a fragment function that contains a widget function, a user triggers a _partial rerun_ instead of a full rerun when they interact with that fragment's widget. During a partial rerun, your fragment function is re-executed. Anything within the main body of your fragment is updated on the frontend while the rest of your app remains the same. We'll describe fragments written across multiple containers later on.

Here is a basic example of defining and calling a fragment function. Just like with caching, remember to call your function after defining it.

```python
import streamlit as st

@st.experimental_fragment
def fragment_function():
if st.button("Hi!"):
st.write("Hi back!")

fragment_function()
```

If you want the main body of your fragment to appear in the sidebar or another container, call your fragment function inside a context manager.

```python
with st.sidebar:
fragment_function()
```

### Partial rerun execution flow

Consider the following code with the explanation and diagram that follow.

```python
import streamlit as st

st.title("My Awesome App")

@st.experimental_fragment()
def toggle_and_text():
cols = st.columns(2)
cols[0].toggle("Toggle")
cols[1].text_area("Enter text")

@st.experimental_fragment()
def filter_and_file():
cols = st.columns(2)
cols[0].checkbox("Filter")
cols[1].file_uploader("Upload image")

toggle_and_text()
cols = st.columns(2)
cols[0].selectbox("Select", [1,2,3], None)
cols[1].button("Update")
filter_and_file()
```

When a user interacts with an input widget inside a fragment, only the fragment reruns instead of the full script. When a user interacts with an input widget outside a fragment, the full script reruns as usual.

If you run the code above, the full script will run top to bottom on your app's initial load. If you flip the toggle button in your running app, the first fragment (`toggle_and_text()`) will rerun which will redraw the toggle and text area while leaving everything else unchanged. If you then click the checkbox, the second fragment (`filter_and_file()`) will rerun and consequently redraw the checkbox and file uploader. Everything else remains unchanged. Finally, if you click the update button, the full script will rerun and Streamlit will redraw everything.

![Diagram of fragment execution flow](/images/concepts/fragment_diagram.png)

## Fragment returns and interacting with the rest of your app

Fragment returns are ignored on reruns so it's not recommended to define return values for your fragment functions. Instead, if your fragment needs to share data with the rest of your app, use Session State. Fragments are just functions in your script, so they can access Session State, imported modules, and other Streamlit elements like containers. If your fragment writes to any container created outside of itself, note the following difference in behavior:

- Elements drawn in the main body of your fragment are cleared and redrawn in place during a fragment rerun. Repeated fragment reruns will not cause additional elements to appear.
- Elements drawn to containers outside the main body of fragment will not be cleared with each fragment rerun. Instead, Streamlit will draw them additively and these elements will accumulate until the next full-script rerun.

To prevent elements from accumulating in outside containers, use [`st.empty`](/develop/api-reference/layout/st.empty) containers. If you need to trigger a full-script rerun from inside a fragment, call [`st.rerun`](/develop/api-reference/execution-flow/st.rerun).

## Automate fragment reruns

`st.experimental_fragment` includes a convenient `run_every` parameter that causes the fragment to rerun automatically at the specified time interval. These reruns are in addition to any reruns (fragment or full-script) triggered by your user. The automatic fragment reruns will continue even if your user is not interacting with your app. This is a great way to show a live data stream or status on a running background job, efficiently updating your rendered data and _only_ your rendered data.

```python
@st.experimental_fragment(run_every="10s")
def auto_function():
# This will update every 10 seconds!
df = get_latest_updates()
st.line_chart(df)

auto_function()
```

## Compare fragments to other Streamlit features

### Fragments vs forms

Here is a comparison between fragments and forms:

- **Forms** allow users to interact with widgets without rerunning your app. Streamlit does not send user actions within a form to your app's Python backend until the form is submitted. Widgets within a form can not dynamically update other widgets (in or out of the form) in real time.
- **Fragments** run independently from the rest of your code. As your users interact with fragment widgets, their actions are immediately processed by your app's Python backend and your fragment code is rerun. Widgets within a fragment can dynamically update other widgets within the same fragment in real time.

A form batches user input, without interaction between any widgets. A fragment immediately processes user input but limits the scope of the rerun.

### Fragments vs callbacks

Here is a comparison between fragments and callbacks:

- **Callbacks** allow you to execute a function at the beginning of a script rerun. A callback is a _single prefix_ to your script rerun.
- **Fragments** allow you to rerun a portion of script. Fragment reruns happen at the end of a script rerun. A fragment is a _repeatable postfix_ to your script.

When callbacks render elements to your page, they are rendered before the rest of your page elements. When fragments renders elements to your page, they are updated with each fragment rerun (unless they are written to containers outside of the fragment, in which case they accumulate there).

### Fragments vs custom components

Here is a comparison between fragments and custom components:

- **Components** are custom frontend code which can interact with the Python code, native elements, and widgets in your Streamlit app. Custom components extend what’s possible with Streamlit. They follow the normal Streamlit execution flow.
- **Fragments** are parts of your app that can rerun independently of the full app. Fragments can be composed of multiple Streamlit elements and widgets or any Python code.

A fragment can include one or more custom components. A custom component could not easily include a fragment!

### Fragments vs caching

Here is a comparison between fragments and caching:

- **Caching:** allows you to skip over a function and return a previously computed value. When you use caching, you execute everything except the cached function (if you've already run it before).
- **Fragments:** allow you to freeze most of your app and just execute the fragment. When you use fragments, you execute only the fragment (when triggering a fragment rerun).

Caching saves you from unnecessarily running a piece of your app while the rest runs. Fragments save you from running all of your app when you only want to run one piece.

## Limitations and unsupported behavior

- Fragments can't detect a change in input values. It is best to use Session State for dynamic input and output for fragment functions.
- Calling fragments within fragments is unsupported.
- Using caching and fragments on the same function is unsupported.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ import streamlit as st
import streamlit.components.v1 as components

# embed streamlit docs in a streamlit app
components.iframe("https://docs.streamlit.io/en/latest")
components.iframe("https://example.com", height=500)
```

## Create a bi-directional component
Expand Down
10 changes: 10 additions & 0 deletions content/develop/quick-references/api-cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,15 @@ st.switch_page("pages/my_page.py")
>>> username = st.text_input("Username")
>>> password = st.text_input("Password")
>>> st.form_submit_button("Login")

# Define a fragment
>>> @st.experimental_fragment
>>> def fragment_function():
>>> df = get_data()
>>> st.line_chart(df)
>>> st.button("Update")
>>>
>>> fragment_function()
```

</CodeTile>
Expand Down Expand Up @@ -348,6 +357,7 @@ st.set_page_config(layout="wide")
st.query_params[key]
st.query_params.get_all(key)
st.query_params.clear()
st.html("<p>Hi!</p>")
```

</CodeTile>
Expand Down
Loading

0 comments on commit 5e9d0c8

Please sign in to comment.