Title: Streamlit - A Deep Dive

  • Streamlit - A Deep Dive
  • Password Protect Streamlit App
  • Intro to Git and GitHub (Version Control)
  • Deploying Streamlit App on Streamlit Community Cloud



# Overview

Now that you have experience with the basics of Streamlit, we will look into some important concepts of Streamlit that have caught some developers off guard and led to unexpected behavior in their applications.

Understanding these concepts is crucial for designing and developing efficient and effective Streamlit apps. In this deep dive, we will explore session state, button behavior, and dynamic widget management, providing you with the knowledge to avoid common pitfalls and enhance your app's functionality.

By having a solid understanding of these elements, you will be better equipped to create user-friendly applications and make the most of Streamlit's features in your projects.

About this Note

This note provides a concise introduction to key "advanced" concepts in Streamlit, aiming to familiarize you with the framework's mechanics and empower you to begin building apps quickly. That's the best way to understand the concepts is to open up your VS Code and try to experiment with the code.

However, it's essential to understand that this is not an exhaustive resource. The official Streamlit documentation remains the definitive guide, offering comprehensive explanations, advanced techniques, and best practices.

To ensure a thorough understanding of Streamlit, we strongly recommend reading the official documentation. We will be including the links to the corresponding section in the official documentation in the different parts of the note below.

We recommend that you first try working with the content in this note. This should be sufficient to get you started. Remember to revisit this note for the links to the official documentation after you have gained some hands-on experience or when you encounter challenges implementing certain things. This approach is more effective.




1 What is State?

Streamlit defines access to a Streamlit app in a browser tab as a session.

  • ✦ For each browser tab that connects to the Streamlit server, a new session is created.
  • ✦ Streamlit reruns your script from top to bottom every time you interact with your app.
  • ✦ Each reruns takes place in a blank slate: no variables are shared between runs.
  • ✦ Widget states are dependent on a particular session (browser connection). The actions of one user do not affect the widgets of any other user.
  • ✦ If a user opens up multiple tabs to access an app, each tab will be a unique session. Changing a widget in one tab will not affect the same widget in another tab.

A session is a single instance of viewing an app. If you view an app from two different tabs in your browser, each tab will have its own session. So each viewer of an app will have a Session State tied to their specific view. Streamlit maintains this session as the user interacts with the app. If the user refreshes their browser page or reloads the URL to the app, their Session State resets and they begin again with a new session.



1.1 Session State

Session State is a way to share variables between reruns, for each user session. In addition to the ability to store and persist state, Streamlit also exposes the ability to manipulate state using Callbacks. Session state also persists across apps inside a multipage app.

Check out this video by Streamlit Developer Advocate Dr. Marisa Smith to on two very essential concepts for Streamlit app development:

  1. session state
  2. callback

Session State provides a dictionary-like interface where you can save information that is preserved between script reruns. Use st.session_state with key or attribute notation to store and recall values. For example, st.session_state["my_key"] or st.session_state.my_key. Remember that widgets handle their statefulness all by themselves, so you won't always need to use Session State!

There are a few common scenarios where Session State is helpful. As demonstrated in the video above, Session State is used when you have a progressive process that you want to build upon from one rerun to the next. Session State can also be used to prevent recalculation, similar to caching. However, the differences are important:

  • ✦ Caching associates stored values to specific functions and inputs. Cached values are accessible to all users across all sessions.
  • ✦ Session State associates stored values to keys (strings). Values in session state are only available in the single session where it was saved.



2 Behaviors of st.button

Buttons created with st.button do not retain state.

They return True on the script rerun resulting from their click and immediately return to Falseon the next script rerun.

  • ✦ If a displayed element is nested inside if st.button('Click me'):
  • ✦ the element will be visible when the button is clicked and disappear as soon as the user takes their next action.
  • ✦ This is because the script reruns and the button return value becomes False.

This note explains the use of buttons and explain common misconceptions.



2.1 When to use if st.button()

When code is conditioned on a button's value, it will execute once in response to the button being clicked and not again (until the button is clicked again).

  • Good to nest inside buttons:

    • Transient messages that immediately disappear.
    • Once-per-click processes that saves data to session state, a file, or a database.
  • Bad to nest inside buttons:

    • Displayed items that should persist as the user continues.
    • Other widgets which cause the script to rerun when used.
    • Processes that neither modify session state nor write to a file/database.*


2.2 Common logic with buttons

2.2.1 Show a temporary message with a button

If you want to give the user a quick button to check if an entry is valid, but not keep that check displayed as the user continues.

  • ✦ In this example, a user can click a button to check if their animal string is in the animal_shelter list.
  • ✦ When the user clicks "Check availability" they will see "We have that animal!" or "We don't have that animal."
  • ✦ If they change the animal in st.text_input, the script reruns and the message disappears until they click "Check availability" again.
import streamlit as st 

animal_shelter = ['cat', 'dog', 'rabbit', 'bird'] 

animal = st.text_input('Type an animal') 

if st.button('Check availability'): 
	have_it = animal.lower() in animal_shelter 
	'We have that animal!' if have_it else 'We don\'t have that animal.'


2.2.2 Stateful button

If you want a clicked button to continue to be True, create a value in st.session_state and use the button to set that value to True in a callback.

import streamlit as st 

if 'clicked' not in st.session_state: 
	st.session_state.clicked = False 
	
def click_button(): 
	st.session_state.clicked = True 
	
st.button('Click me', on_click=click_button) 

if st.session_state.clicked: 
	# The message and nested widget will remain on the page 
	st.write('Button clicked!') 
	st.slider('Select a value')


2.2.3 Buttons to modify st.session_state

If you modify st.session_state inside of a button, you must consider where that button is within the script.

2.2.3.1 A slight problem

In this example, we access st.session_state.name both before and after the buttons which modify it. When a button ("Jane" or "John") is clicked, the script reruns. The info displayed before the buttons lags behind the info written after the button. The data in st.session_state before the button is not updated. When the script executes the button function, that is when the conditional code to update st.session_state creates the change. Thus, this change is reflected after the button.

import streamlit as st 
import pandas as pd 

if 'name' not in st.session_state: 
	st.session_state['name'] = 'John Doe' 
	
st.header(st.session_state['name']) 

if st.button('Jane'): 
	st.session_state['name'] = 'Jane Doe' 
	
if st.button('John'): 
	st.session_state['name'] = 'John Doe' 
	
st.header(st.session_state['name'])

2.2.3.2 Logic used in a callback

Callbacks are a clean way to modify st.session_state. Callbacks are executed as a prefix to the script rerunning, so the position of the button relative to accessing data is not important.


import streamlit as st 
import pandas as pd 

if 'name' not in st.session_state: 
	st.session_state['name'] = 'John Doe' 
	
def change_name(name): 
	st.session_state['name'] = name 
	
st.header(st.session_state['name']) 

st.button('Jane', on_click=change_name, args=['Jane Doe']) 
st.button('John', on_click=change_name, args=['John Doe']) 

st.header(st.session_state['name'])


2.2.4 Buttons to modify or reset other widgets

When a button is used to modify or reset another widget, it is the same as the above examples to modify st.session_state. However, an extra consideration exists: you cannot modify a key-value pair in st.session_state if the widget with that key has already been rendered on the page for the current script run.

Don't do this!
import streamlit as st 
st.text_input('Name', key='name') 

# These buttons will error because their nested code changes 
# a widget's state after that widget within the script. 
if st.button('Clear name'): 
	st.session_state.name = '' 
	
if st.button('Streamlit!'): 
	st.session_state.name = ('Streamlit')

2.2.4.1 Option 1: Use a key for the button and put the logic before the widget

If you assign a key to a button, you can condition code on a button's state by using its value in st.session_state. This means that logic depending on your button can be in your script before that button.

In the following example, we use the .get() method on st.session_state because the keys for the buttons will not exist when the script runs for the first time. The .get() method will return False if it can't find the key. Otherwise, it will return the value of the key.

import streamlit as st 

# Use the get method since the keys won't be in session_state 
# on the first script run 
if st.session_state.get('clear'): 
	st.session_state['name'] = '' 
	
if st.session_state.get('streamlit'): 
	st.session_state['name'] = 'Streamlit' 
	
st.text_input('Name', key='name') 

st.button('Clear name', key='clear') 
st.button('Streamlit!', key='streamlit')

2.2.4.2 Option 2: Use a callback

import streamlit as st 

st.text_input('Name', key='name') 

def set_name(name): 
	st.session_state.name = name 
	
st.button('Clear name', on_click=set_name, args=['']) 
st.button('Streamlit!', on_click=set_name, args=['Streamlit'])`

2.2.4.3 Option 3: Use containers

By using st.container you can have widgets appear in different orders in your script and frontend view (webpage).

import streamlit as st 

begin = st.container() 

if st.button('Clear name'): 
	st.session_state.name = '' 
	
if st.button('Streamlit!'): 
	st.session_state.name = ('Streamlit') 
	
# The widget is second in logic, but first in display 
begin.text_input('Name', key='name')
Relevant Sections in Official Documentation



3 Secrets Management


Storing unencrypted secrets in a git repository is a bad practice. For applications that require access to sensitive credentials, the recommended solution is to store those credentials outside the repository - such as using a credentials file not committed to the repository or passing them as environment variables.

Streamlit provides native file-based secrets management to easily store and securely access your secrets in your Streamlit app.



3.1 How to use secrets management

3.1.1 Develop locally and set up secrets

Create a file secrets.toml in the $CWD/.streamlit folder where $CWD is the folder you're running Streamlit from. For example, $CWD/.streamlit/secrets.toml.

# Everything in this section will be available as an environment variable 
OPENAPI_API_KEY = "sk-1241infjsenfuhnh81n8cdn"

# You can also add other sections if you like. 
# The contents of sections as shown below will not become environment variables, # but they'll be easily accessible from within Streamlit anyway as we show 
# later. 
[my_other_secrets] 
things_i_like = ["Streamlit", "Python"]
Warning
  • Add this file to your .gitignore so you don't upload your secrets to online repository
  • We will look at how to do this in later section of this topic.

3.1.2 Use secrets in your app

Access your secrets by querying the st.secrets dict, or as environment variables. For example, if you enter the secrets from the section above, the code below shows you how to access them within your Streamlit app

import streamlit as st 

# Everything is accessible via the st.secrets dict:
st.write("OPENAPI_API_KEY:", st.secrets["OPENAPI_API_KEY"]) 

# Store the value in "Environment Variable"
# This is required by some packages like LangChain
os.environ['OPENAPI_API_KEY'] = st.secrets['OPENAPI_API_KEY']
Use secrets on Streamlit Community Cloud
  • This is the default deployment method for your Streamlit application in this Bootcamp
  • When you deploy your app to Streamlit Community Cloud, you can use the same secrets management workflow as you would locally.
  • However, you'll need to also set up your secrets in the Community Cloud Secrets Management console. Learn how to do so via the Cloud-specific Secrets management documentation.
  • We will cover how to do this in our Walkthrough later.