I run the code by using Python 3.9 and streamlit==1.32.0. I have the next code snippet -
import threading
import time
import os
import ctypes
import streamlit as st
from streamlit_option_menu import option_menu
from syngen.streamlit_app.handlers import StreamlitHandler
from syngen.streamlit_app.utils import (
show_data,
get_running_status,
set_session_state,
cleanup_artifacts
)
class ThreadWithException(threading.Thread):
"""
A class for a thread with the ability to raise an exception
"""
def get_id(self):
if hasattr(self, "_ident"):
return self._ident
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
def stop_running_thread():
"""
Stop the running thread
"""
thread = [
thread
for thread in threading.enumerate()
if thread.name == "train_and_infer"
]
if thread:
runner = thread[0]
runner.raise_exception()
runner.join()
return runner
return None
def run_basic_page():
"""
Run the basic page of the Streamlit app
"""
set_session_state()
uploaded_file = st.file_uploader(
"Upload a CSV file",
type="csv",
accept_multiple_files=False,
disabled=get_running_status()
)
if not uploaded_file:
runner = stop_running_thread()
if runner and not runner.is_alive():
st.warning("The training and the generation were interrupted")
cleanup_artifacts()
st.warning("Please upload a CSV file to proceed")
if uploaded_file:
show_data(uploaded_file)
epochs = st.number_input(
"Epochs",
min_value=1,
value=1,
disabled=get_running_status()
)
size_limit = st.number_input(
"Rows to generate",
min_value=1,
max_value=None,
value=1000,
disabled=get_running_status()
)
print_report = st.checkbox(
"Create an accuracy report",
value=False,
key="print_report",
disabled=get_running_status()
)
app = StreamlitHandler(epochs, size_limit, print_report, uploaded_file)
if st.button(
"Generate data", type="primary", key="gen_button", disabled=get_running_status()
):
runner = ThreadWithException(name="train_and_infer", target=app.train_and_infer)
runner.start()
current_progress = 0
prg = st.progress(current_progress)
while runner.is_alive():
with st.expander("Logs"):
while True:
if not app.log_queue.empty():
with st.code("logs", language="log"):
log = app.log_queue.get()
st.text(log)
current_progress, message = app.progress_handler.info
prg.progress(value=current_progress, text=message)
elif not app.log_error_queue.empty():
runner.raise_exception()
runner.join()
break
elif not runner.is_alive():
break
time.sleep(0.001)
if not app.log_error_queue.empty() and not runner.is_alive():
st.exception(app.log_error_queue.get())
elif app.log_queue.empty() and not runner.is_alive():
prg.progress(100)
st.success("Data generation completed")
with st.container():
app.generate_buttons()
def run():
"""
Run the Streamlit app
"""
with st.sidebar:
selected = option_menu("", ["Basic"],
icons=["'play'"],
default_index=0,
menu_icon=None,
styles={
"container": {"font-family": "Open Sans"}
}
)
if selected == "Basic":
run_basic_page()
if __name__ == "__main__":
run()
If the running thread is interrupted, it will be killed, but in the next running thread, the error log messages from the previous thread are sent and cause the error. Could you please provide the reasons why it happens?