2022-05-23 00:16:32 +04:00

509 lines
15 KiB
Python

# Copyright 2018-2022 Streamlit Inc.
#
# 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.
"""Streamlit.
How to use Streamlit in 3 seconds:
1. Write an app
>>> import streamlit as st
>>> st.write(anything_you_want)
2. Run your app
$ streamlit run my_script.py
3. Use your app
A new tab will open on your browser. That's your Streamlit app!
4. Modify your code, save it, and watch changes live on your browser.
Take a look at the other commands in this module to find out what else
Streamlit can do:
>>> dir(streamlit)
Or try running our "Hello World":
$ streamlit hello
For more detailed info, see https://docs.streamlit.io.
"""
# IMPORTANT: Prefix with an underscore anything that the user shouldn't see.
# Must be at the top, to avoid circular dependency.
from streamlit import logger as _logger
from streamlit import config as _config
from streamlit.proto.RootContainer_pb2 import RootContainer
from streamlit.secrets import Secrets, SECRETS_FILE_LOC
_LOGGER = _logger.get_logger("root")
# Give the package a version.
from importlib_metadata import version as _version
__version__ = _version("streamlit")
from typing import NoReturn
import contextlib as _contextlib
import sys as _sys
import threading as _threading
import urllib.parse as _parse
import click as _click
from streamlit import code_util as _code_util
from streamlit import env_util as _env_util
from streamlit import source_util as _source_util
from streamlit import string_util as _string_util
from streamlit.delta_generator import DeltaGenerator as _DeltaGenerator
from streamlit.scriptrunner import (
add_script_run_ctx as _add_script_run_ctx,
get_script_run_ctx as _get_script_run_ctx,
StopException,
RerunException as _RerunException,
RerunData as _RerunData,
)
from streamlit.errors import StreamlitAPIException
from streamlit.proto import ForwardMsg_pb2 as _ForwardMsg_pb2
# Modules that the user should have access to. These are imported with "as"
# syntax pass mypy checking with implicit_reexport disabled.
from streamlit.echo import echo as echo
from streamlit.legacy_caching import cache as cache
from streamlit.caching import singleton as experimental_singleton
from streamlit.caching import memo as experimental_memo
# This is set to True inside cli._main_run(), and is False otherwise.
# If False, we should assume that DeltaGenerator functions are effectively
# no-ops, and adapt gracefully.
_is_running_with_streamlit = False
def _update_logger():
_logger.set_log_level(_config.get_option("logger.level").upper())
_logger.update_formatter()
_logger.init_tornado_logs()
# Make this file only depend on config option in an asynchronous manner. This
# avoids a race condition when another file (such as a test file) tries to pass
# in an alternative config.
_config.on_config_parsed(_update_logger, True)
_main = _DeltaGenerator(root_container=RootContainer.MAIN)
sidebar = _DeltaGenerator(root_container=RootContainer.SIDEBAR, parent=_main)
secrets = Secrets(SECRETS_FILE_LOC)
# DeltaGenerator methods:
altair_chart = _main.altair_chart
area_chart = _main.area_chart
audio = _main.audio
balloons = _main.balloons
bar_chart = _main.bar_chart
bokeh_chart = _main.bokeh_chart
button = _main.button
caption = _main.caption
camera_input = _main.camera_input
checkbox = _main.checkbox
code = _main.code
columns = _main.columns
container = _main.container
dataframe = _main.dataframe
date_input = _main.date_input
download_button = _main.download_button
expander = _main.expander
pydeck_chart = _main.pydeck_chart
empty = _main.empty
error = _main.error
exception = _main.exception
file_uploader = _main.file_uploader
form = _main.form
form_submit_button = _main.form_submit_button
graphviz_chart = _main.graphviz_chart
header = _main.header
help = _main.help
image = _main.image
info = _main.info
json = _main.json
latex = _main.latex
line_chart = _main.line_chart
map = _main.map
markdown = _main.markdown
metric = _main.metric
multiselect = _main.multiselect
number_input = _main.number_input
plotly_chart = _main.plotly_chart
progress = _main.progress
pyplot = _main.pyplot
radio = _main.radio
selectbox = _main.selectbox
select_slider = _main.select_slider
slider = _main.slider
snow = _main.snow
subheader = _main.subheader
success = _main.success
table = _main.table
text = _main.text
text_area = _main.text_area
text_input = _main.text_input
time_input = _main.time_input
title = _main.title
vega_lite_chart = _main.vega_lite_chart
video = _main.video
warning = _main.warning
write = _main.write
color_picker = _main.color_picker
# Legacy
_legacy_dataframe = _main._legacy_dataframe
_legacy_table = _main._legacy_table
_legacy_altair_chart = _main._legacy_altair_chart
_legacy_area_chart = _main._legacy_area_chart
_legacy_bar_chart = _main._legacy_bar_chart
_legacy_line_chart = _main._legacy_line_chart
_legacy_vega_lite_chart = _main._legacy_vega_lite_chart
# Apache Arrow
_arrow_dataframe = _main._arrow_dataframe
_arrow_table = _main._arrow_table
_arrow_altair_chart = _main._arrow_altair_chart
_arrow_area_chart = _main._arrow_area_chart
_arrow_bar_chart = _main._arrow_bar_chart
_arrow_line_chart = _main._arrow_line_chart
_arrow_vega_lite_chart = _main._arrow_vega_lite_chart
# Config
get_option = _config.get_option
from streamlit.commands.page_config import set_page_config
# Session State
from streamlit.state import SessionStateProxy
session_state = SessionStateProxy()
# Beta APIs
beta_container = _main.beta_container
beta_expander = _main.beta_expander
beta_columns = _main.beta_columns
def set_option(key, value):
"""Set config option.
Currently, only the following config options can be set within the script itself:
* client.caching
* client.displayEnabled
* deprecation.*
Calling with any other options will raise StreamlitAPIException.
Run `streamlit config show` in the terminal to see all available options.
Parameters
----------
key : str
The config option key of the form "section.optionName". To see all
available options, run `streamlit config show` on a terminal.
value
The new value to assign to this config option.
"""
opt = _config._config_options_template[key]
if opt.scriptable:
_config.set_option(key, value)
return
raise StreamlitAPIException(
"{key} cannot be set on the fly. Set as command line option, e.g. streamlit run script.py --{key}, or in config.toml instead.".format(
key=key
)
)
def experimental_show(*args):
"""Write arguments and *argument names* to your app for debugging purposes.
Show() has similar properties to write():
1. You can pass in multiple arguments, all of which will be debugged.
2. It returns None, so it's "slot" in the app cannot be reused.
Note: This is an experimental feature. See
https://docs.streamlit.io/library/advanced-features/prerelease#experimental for more information.
Parameters
----------
*args : any
One or many objects to debug in the App.
Example
-------
>>> dataframe = pd.DataFrame({
... 'first column': [1, 2, 3, 4],
... 'second column': [10, 20, 30, 40],
... })
>>> st.experimental_show(dataframe)
Notes
-----
This is an experimental feature with usage limitations:
- The method must be called with the name `show`.
- Must be called in one line of code, and only once per line.
- When passing multiple arguments the inclusion of `,` or `)` in a string
argument may cause an error.
"""
if not args:
return
try:
import inspect
# Get the calling line of code
current_frame = inspect.currentframe()
if current_frame is None:
warning("`show` not enabled in the shell")
return
if current_frame.f_back is not None:
lines = inspect.getframeinfo(current_frame.f_back)[3]
else:
lines = None
if not lines:
warning("`show` not enabled in the shell")
return
# Parse arguments from the line
line = lines[0].split("show", 1)[1]
inputs = _code_util.get_method_args_from_code(args, line)
# Escape markdown and add deltas
for idx, input in enumerate(inputs):
escaped = _string_util.escape_markdown(input)
markdown("**%s**" % escaped)
write(args[idx])
except Exception:
_, exc, exc_tb = _sys.exc_info()
exception(exc)
def experimental_get_query_params():
"""Return the query parameters that is currently showing in the browser's URL bar.
Returns
-------
dict
The current query parameters as a dict. "Query parameters" are the part of the URL that comes
after the first "?".
Example
-------
Let's say the user's web browser is at
`http://localhost:8501/?show_map=True&selected=asia&selected=america`.
Then, you can get the query parameters using the following:
>>> st.experimental_get_query_params()
{"show_map": ["True"], "selected": ["asia", "america"]}
Note that the values in the returned dict are *always* lists. This is
because we internally use Python's urllib.parse.parse_qs(), which behaves
this way. And this behavior makes sense when you consider that every item
in a query string is potentially a 1-element array.
"""
ctx = _get_script_run_ctx()
if ctx is None:
return ""
return _parse.parse_qs(ctx.query_string)
def experimental_set_query_params(**query_params):
"""Set the query parameters that are shown in the browser's URL bar.
Parameters
----------
**query_params : dict
The query parameters to set, as key-value pairs.
Example
-------
To point the user's web browser to something like
"http://localhost:8501/?show_map=True&selected=asia&selected=america",
you would do the following:
>>> st.experimental_set_query_params(
... show_map=True,
... selected=["asia", "america"],
... )
"""
ctx = _get_script_run_ctx()
if ctx is None:
return
ctx.query_string = _parse.urlencode(query_params, doseq=True)
msg = _ForwardMsg_pb2.ForwardMsg()
msg.page_info_changed.query_string = ctx.query_string
ctx.enqueue(msg)
@_contextlib.contextmanager
def spinner(text="In progress..."):
"""Temporarily displays a message while executing a block of code.
Parameters
----------
text : str
A message to display while executing that block
Example
-------
>>> with st.spinner('Wait for it...'):
>>> time.sleep(5)
>>> st.success('Done!')
"""
import streamlit.legacy_caching.caching as legacy_caching
import streamlit.caching as caching
from streamlit.elements.utils import clean_text
from streamlit.proto.Spinner_pb2 import Spinner as SpinnerProto
# @st.cache optionally uses spinner for long-running computations.
# Normally, streamlit warns the user when they call st functions
# from within an @st.cache'd function. But we do *not* want to show
# these warnings for spinner's message, so we create and mutate this
# message delta within the "suppress_cached_st_function_warning"
# context.
with legacy_caching.suppress_cached_st_function_warning():
with caching.suppress_cached_st_function_warning():
message = empty()
try:
# Set the message 0.1 seconds in the future to avoid annoying
# flickering if this spinner runs too quickly.
DELAY_SECS = 0.1
display_message = True
display_message_lock = _threading.Lock()
def set_message():
with display_message_lock:
if display_message:
with legacy_caching.suppress_cached_st_function_warning():
with caching.suppress_cached_st_function_warning():
spinner_proto = SpinnerProto()
spinner_proto.text = clean_text(text)
message._enqueue("spinner", spinner_proto)
_add_script_run_ctx(_threading.Timer(DELAY_SECS, set_message)).start()
# Yield control back to the context.
yield
finally:
if display_message_lock:
with display_message_lock:
display_message = False
with legacy_caching.suppress_cached_st_function_warning():
with caching.suppress_cached_st_function_warning():
message.empty()
def _transparent_write(*args):
"""This is just st.write, but returns the arguments you passed to it."""
write(*args)
if len(args) == 1:
return args[0]
return args
# We want to show a warning when the user runs a Streamlit script without
# 'streamlit run', but we need to make sure the warning appears only once no
# matter how many times __init__ gets loaded.
_use_warning_has_been_displayed = False
def _maybe_print_use_warning():
"""Print a warning if Streamlit is imported but not being run with `streamlit run`.
The warning is printed only once.
"""
global _use_warning_has_been_displayed
if not _use_warning_has_been_displayed:
_use_warning_has_been_displayed = True
warning = _click.style("Warning:", bold=True, fg="yellow")
if _env_util.is_repl():
_LOGGER.warning(
f"\n {warning} to view a Streamlit app on a browser, use Streamlit in a file and\n run it with the following command:\n\n streamlit run [FILE_NAME] [ARGUMENTS]"
)
elif not _is_running_with_streamlit and _config.get_option(
"global.showWarningOnDirectExecution"
):
script_name = _sys.argv[0]
_LOGGER.warning(
f"\n {warning} to view this Streamlit app on a browser, run it with the following\n command:\n\n streamlit run {script_name} [ARGUMENTS]"
)
def stop() -> NoReturn:
"""Stops execution immediately.
Streamlit will not run any statements after `st.stop()`.
We recommend rendering a message to explain why the script has stopped.
When run outside of Streamlit, this will raise an Exception.
Example
-------
>>> name = st.text_input('Name')
>>> if not name:
>>> st.warning('Please input a name.')
>>> st.stop()
>>> st.success('Thank you for inputting a name.')
"""
raise StopException()
def experimental_rerun():
"""Rerun the script immediately.
When `st.experimental_rerun()` is called, the script is halted - no
more statements will be run, and the script will be queued to re-run
from the top.
If this function is called outside of Streamlit, it will raise an
Exception.
"""
ctx = _get_script_run_ctx()
query_string = "" if ctx is None else ctx.query_string
raise _RerunException(_RerunData(query_string=query_string))