Webapp goes blank

I find sometimes the webapp goes blank moments after starting.

Ie.

  1. Start the app
  2. I can see the app for a fraction of a second
  3. Webapp goes completely white

I found this webapp has the issue:

https://share.streamlit.io/pablocfonseca/streamlit-aggrid/main/examples/example.py

Code can be found here:
https://raw.githubusercontent.com/PablocFonseca/streamlit-aggrid/main/examples/example.py

I see the same issue.
Seems the web socket connection is interrupted:

1 Like

I seem to have traced the issue to this line of code

st.dataframe(grid_response['data'])

which is line 183 here:

import streamlit as st
import pandas as pd 
import numpy as np
import altair as alt
from itertools import cycle

from st_aggrid import GridOptionsBuilder, AgGrid, GridUpdateMode, DataReturnMode, JsCode

np.random.seed(42)

@st.cache(allow_output_mutation=True)
def fetch_data(samples):
    deltas = cycle([
            pd.Timedelta(weeks=-2),
            pd.Timedelta(days=-1),
            pd.Timedelta(hours=-1),
            pd.Timedelta(0),
            pd.Timedelta(minutes=5),
            pd.Timedelta(seconds=10),
            pd.Timedelta(microseconds=50),
            pd.Timedelta(microseconds=10)
            ])
    dummy_data = {
        "date_time_naive":pd.date_range('2021-01-01', periods=samples),
        "apple":np.random.randint(0,100,samples) / 3.0,
        "banana":np.random.randint(0,100,samples) / 5.0,
        "chocolate":np.random.randint(0,100,samples),
        "group": np.random.choice(['A','B'], size=samples),
        "date_only":pd.date_range('2020-01-01', periods=samples).date,
        "timedelta":[next(deltas) for i in range(samples)],
        "date_tz_aware":pd.date_range('2022-01-01', periods=samples, tz="Asia/Katmandu")
    }
    return pd.DataFrame(dummy_data)

#Example controlers
st.sidebar.subheader("St-AgGrid example options")

sample_size = st.sidebar.number_input("rows", min_value=10, value=10)
grid_height = st.sidebar.number_input("Grid height", min_value=200, max_value=800, value=200)

return_mode = st.sidebar.selectbox("Return Mode", list(DataReturnMode.__members__), index=1)
return_mode_value = DataReturnMode.__members__[return_mode]

update_mode = st.sidebar.selectbox("Update Mode", list(GridUpdateMode.__members__), index=6)
update_mode_value = GridUpdateMode.__members__[update_mode]

#enterprise modules
enable_enterprise_modules = st.sidebar.checkbox("Enable Enterprise Modules")
if enable_enterprise_modules:
    enable_sidebar =st.sidebar.checkbox("Enable grid sidebar", value=False)
else:
    enable_sidebar = False

#features
fit_columns_on_grid_load = st.sidebar.checkbox("Fit Grid Columns on Load")

enable_selection=st.sidebar.checkbox("Enable row selection", value=True)
if enable_selection:
    st.sidebar.subheader("Selection options")
    selection_mode = st.sidebar.radio("Selection Mode", ['single','multiple'])
    
    use_checkbox = st.sidebar.checkbox("Use check box for selection")
    if use_checkbox:
        groupSelectsChildren = st.sidebar.checkbox("Group checkbox select children", value=True)
        groupSelectsFiltered = st.sidebar.checkbox("Group checkbox includes filtered", value=True)

    if ((selection_mode == 'multiple') & (not use_checkbox)):
        rowMultiSelectWithClick = st.sidebar.checkbox("Multiselect with click (instead of holding CTRL)", value=False)
        if not rowMultiSelectWithClick:
            suppressRowDeselection = st.sidebar.checkbox("Suppress deselection (while holding CTRL)", value=False)
        else:
            suppressRowDeselection=False
    st.sidebar.text("___")

enable_pagination = st.sidebar.checkbox("Enable pagination", value=False)
if enable_pagination:
    st.sidebar.subheader("Pagination options")
    paginationAutoSize = st.sidebar.checkbox("Auto pagination size", value=True)
    if not paginationAutoSize:
        paginationPageSize = st.sidebar.number_input("Page size", value=5, min_value=0, max_value=sample_size)
    st.sidebar.text("___")

df = fetch_data(sample_size)

#Infer basic colDefs from dataframe types
gb = GridOptionsBuilder.from_dataframe(df)

#customize gridOptions
gb.configure_default_column(groupable=True, value=True, enableRowGroup=True, aggFunc='sum', editable=True)

gb.configure_column("date_tz_aware", type=["dateColumnFilter","customDateTimeFormat"], custom_format_string='yyyy-MM-dd HH:mm zzz', pivot=True)

gb.configure_column("apple", type=["numericColumn","numberColumnFilter","customNumericFormat"], precision=2, aggFunc='sum')
gb.configure_column("banana", type=["numericColumn", "numberColumnFilter", "customNumericFormat"], precision=1, aggFunc='avg')
gb.configure_column("chocolate", type=["numericColumn", "numberColumnFilter", "customCurrencyFormat"], custom_currency_symbol="R$", aggFunc='max')

#configures last row to use custom styles based on cell's value, injecting JsCode on components front end
cellsytle_jscode = JsCode("""
function(params) {
    if (params.value == 'A') {
        return {
            'color': 'white',
            'backgroundColor': 'darkred'
        }
    } else {
        return {
            'color': 'black',
            'backgroundColor': 'white'
        }
    }
};
""")
gb.configure_column("group", cellStyle=cellsytle_jscode)

if enable_sidebar:
    gb.configure_side_bar()

if enable_selection:
    gb.configure_selection(selection_mode)
    if use_checkbox:
        gb.configure_selection(selection_mode, use_checkbox=True, groupSelectsChildren=groupSelectsChildren, groupSelectsFiltered=groupSelectsFiltered)
    if ((selection_mode == 'multiple') & (not use_checkbox)):
        gb.configure_selection(selection_mode, use_checkbox=False, rowMultiSelectWithClick=rowMultiSelectWithClick, suppressRowDeselection=suppressRowDeselection)

if enable_pagination:
    if paginationAutoSize:
        gb.configure_pagination(paginationAutoPageSize=True)
    else:
        gb.configure_pagination(paginationAutoPageSize=False, paginationPageSize=paginationPageSize)

gb.configure_grid_options(domLayout='normal')
gridOptions = gb.build()

#Display the grid
st.header("Streamlit Ag-Grid")
st.markdown("""
    AgGrid can handle many types of columns and will try to render the most human readable way.  
    On editions, grid will fallback to string representation of data, DateTime and TimeDeltas are converted to ISO format.
    Custom display formating may be applied to numeric fields, but returned data will still be numeric.
""")

grid_response = AgGrid(
    df, 
    gridOptions=gridOptions,
    height=grid_height, 
    width='100%',
    data_return_mode=return_mode_value, 
    update_mode=update_mode_value,
    fit_columns_on_grid_load=fit_columns_on_grid_load,
    allow_unsafe_jscode=True, #Set it to True to allow jsfunction to be injected
    enable_enterprise_modules=enable_enterprise_modules,
    )

df = grid_response['data']
selected = grid_response['selected_rows']
selected_df = pd.DataFrame(selected)

with st.spinner("Displaying results..."):
    #displays the chart
    chart_data = df.loc[:,['date_time_naive','apple','banana','chocolate']].assign(source='total')

    if not selected_df.empty:
        selected_data = selected_df.loc[:,['date_time_naive','apple','banana','chocolate']].assign(source='selection')
        chart_data = pd.concat([chart_data, selected_data])

    chart_data = pd.melt(chart_data, id_vars=['date_time_naive','source'], var_name="item", value_name="quantity")
    #st.dataframe(chart_data)
    chart = alt.Chart(data=chart_data).mark_bar().encode(
        x=alt.X("item:O"),
        y=alt.Y("sum(quantity):Q", stack=False),
        color=alt.Color('source:N', scale=alt.Scale(domain=['total','selection'])),
    )

    st.header("Component Outputs - Example chart")
    st.markdown("""
    This chart is built with data returned from the grid. rows that are selected are also identified.
    Experiment selecting rows, group and filtering and check how the chart updates to match.
    """)

    st.altair_chart(chart, use_container_width=True)

    st.subheader("Returned grid data:")
    st.dataframe(grid_response['data'])

    st.subheader("grid selection:")
    st.write(grid_response['selected_rows'])

    st.header("Generated gridOptions")
    st.markdown("""
        All grid configuration is done thorugh a dictionary passed as ```gridOptions``` parameter to AgGrid call.
        You can build it yourself, or use ```gridOptionBuilder``` helper class.  
        Ag-Grid documentation can be read [here](https://www.ag-grid.com/documentation)
    """)
    st.write(gridOptions)

I tried replacing
st.dataframe(grid_response['data'])
with
st.write(grid_response['data'])
but no change.

I checked the dataframe size and it’s only 10 rows, 8 columns.

The table contains these types:

<class 'pandas._libs.tslibs.timestamps.Timestamp'>
<class 'float'>
<class 'int'>
<class 'str'>
<class 'datetime.date'>
<class 'pandas._libs.tslibs.timedeltas.Timedelta'>

And I found that if I drop the column timedelta with type timedelta64[ns] then the issue is solved.

Very interestingly, if I do st.write(data.dtypes) I get this error:

ArrowInvalid: ("Could not convert dtype('<M8[ns]') with type numpy.dtype: 
did not recognize Python value type when inferring an Arrow data type", 
'Conversion failed for column 0 with type object')

Traceback:
File "/usr/local/lib/python3.9/site-packages/streamlit/script_runner.py", line 354, in _run_script
    exec(code, module.__dict__)
File "/Users/mkleinbort/streamlit-showcase/qpp.py", line 199, in <module>
    st.write(x.dtypes)
File "/usr/local/lib/python3.9/site-packages/streamlit/elements/write.py", line 182, in write
    self.dg.dataframe(arg)
File "/usr/local/lib/python3.9/site-packages/streamlit/elements/dataframe_selector.py", line 85, in dataframe
    return self.dg._arrow_dataframe(data, width, height)
File "/usr/local/lib/python3.9/site-packages/streamlit/elements/arrow.py", line 82, in _arrow_dataframe
    marshall(proto, data, default_uuid)
File "/usr/local/lib/python3.9/site-packages/streamlit/elements/arrow.py", line 160, in marshall
    proto.data = type_util.data_frame_to_bytes(df)
File "/usr/local/lib/python3.9/site-packages/streamlit/type_util.py", line 372, in data_frame_to_bytes
    table = pa.Table.from_pandas(df)
File "pyarrow/table.pxi", line 1561, in pyarrow.lib.Table.from_pandas
File "/usr/local/lib/python3.9/site-packages/pyarrow/pandas_compat.py", line 594, in dataframe_to_arrays
    arrays = [convert_column(c, f)
File "/usr/local/lib/python3.9/site-packages/pyarrow/pandas_compat.py", line 594, in <listcomp>
    arrays = [convert_column(c, f)
File "/usr/local/lib/python3.9/site-packages/pyarrow/pandas_compat.py", line 581, in convert_column
    raise e
File "/usr/local/lib/python3.9/site-packages/pyarrow/pandas_compat.py", line 575, in convert_column
    result = pa.array(col, type=type_, from_pandas=True, safe=safe)
File "pyarrow/array.pxi", line 302, in pyarrow.lib.array
File "pyarrow/array.pxi", line 83, in pyarrow.lib._ndarray_to_array
File "pyarrow/error.pxi", line 99, in pyarrow.lib.check_status

In short, here is a minimal example that breaks streamlit:

import streamlit as st
import pandas as pd 

df = pd.DataFrame({'timedelta':[pd.Timedelta(0)]})
st.write(df)

Interresting. Think it’s a good idea to raise an issue on this on github:

1 Like

Yep

1 Like