def progress(percent): progress_msg = ForwardMsg() progress_msg.upload_report_progress = percent yield ws.write_message(serialize_forward_msg(progress_msg), binary=True)
def _create_dataframe_msg(df, id=1): msg = ForwardMsg() msg.metadata.delta_id = id msg.metadata.parent_block.container = BlockPath.SIDEBAR data_frame_proto.marshall_data_frame(df, msg.delta.new_element.data_frame) return msg
def _on_scriptrunner_event(self, event, exception=None, client_state=None): """Called when our ScriptRunner emits an event. This is *not* called on the main thread. Parameters ---------- event : ScriptRunnerEvent exception : BaseException | None An exception thrown during compilation. Set only for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event. client_state : streamlit.proto.ClientState_pb2.ClientState | None The ScriptRunner's final ClientState. Set only for the SHUTDOWN event. """ LOGGER.debug("OnScriptRunnerEvent: %s", event) prev_state = self._state if event == ScriptRunnerEvent.SCRIPT_STARTED: if self._state != ReportSessionState.SHUTDOWN_REQUESTED: self._state = ReportSessionState.REPORT_IS_RUNNING if config.get_option("server.liveSave"): # Enqueue into the IOLoop so it runs without blocking AND runs # on the main thread. self._ioloop.spawn_callback(self._save_running_report) self._clear_queue() self._maybe_enqueue_initialize_message() self._enqueue_new_report_message() elif ( event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR ): if self._state != ReportSessionState.SHUTDOWN_REQUESTED: self._state = ReportSessionState.REPORT_NOT_RUNNING script_succeeded = event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS self._enqueue_report_finished_message( ForwardMsg.FINISHED_SUCCESSFULLY if script_succeeded else ForwardMsg.FINISHED_WITH_COMPILE_ERROR ) if config.get_option("server.liveSave"): # Enqueue into the IOLoop so it runs without blocking AND runs # on the main thread. self._ioloop.spawn_callback(self._save_final_report_and_quit) if script_succeeded: # When a script completes successfully, we update our # LocalSourcesWatcher to account for any source code changes # that change which modules should be watched. (This is run on # the main thread, because LocalSourcesWatcher is not # thread safe.) self._ioloop.spawn_callback( self._local_sources_watcher.update_watched_modules ) else: # When a script fails to compile, we send along the exception. from streamlit.elements import exception_proto msg = ForwardMsg() exception_proto.marshall( msg.session_event.script_compilation_exception, exception ) self.enqueue(msg) elif event == ScriptRunnerEvent.SHUTDOWN: # When ScriptRunner shuts down, update our local reference to it, # and check to see if we need to spawn a new one. (This is run on # the main thread.) if self._state == ReportSessionState.SHUTDOWN_REQUESTED: # Only clear media files if the script is done running AND the # report session is actually shutting down. media_file_manager.clear_session_files(self.id) def on_shutdown(): self._client_state = client_state self._scriptrunner = None # Because a new ScriptEvent could have been enqueued while the # scriptrunner was shutting down, we check to see if we should # create a new one. (Otherwise, a newly-enqueued ScriptEvent # won't be processed until another event is enqueued.) self._maybe_create_scriptrunner() self._ioloop.spawn_callback(on_shutdown) # Send a message if our run state changed report_was_running = prev_state == ReportSessionState.REPORT_IS_RUNNING report_is_running = self._state == ReportSessionState.REPORT_IS_RUNNING if report_is_running != report_was_running: self._enqueue_session_state_changed_message()
def _create_dataframe_msg(df, id=1) -> ForwardMsg: msg = ForwardMsg() msg.metadata.delta_path[:] = make_delta_path(RootContainer.SIDEBAR, (), id) data_frame.marshall_data_frame(df, msg.delta.new_element.data_frame) return msg
def _create_dataframe_msg(df, id=1): msg = ForwardMsg() msg.metadata.delta_path[:] = [RootContainer.SIDEBAR, id] data_frame_proto.marshall_data_frame(df, msg.delta.new_element.data_frame) return msg
def _on_scriptrunner_event( self, sender: Optional[ScriptRunner], event: ScriptRunnerEvent, exception: Optional[BaseException] = None, client_state: Optional[ClientState] = None, ) -> None: """Called when our ScriptRunner emits an event. This is called from the sender ScriptRunner's script thread; it is *not* called on the main thread. Parameters ---------- sender : ScriptRunner | None The ScriptRunner that emitted the event. This will be set to None when called from `handle_backmsg_exception`. event : ScriptRunnerEvent The event type. exception : BaseException | None An exception thrown during compilation. Set only for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event. client_state : streamlit.proto.ClientState_pb2.ClientState | None The ScriptRunner's final ClientState. Set only for the SHUTDOWN event. """ LOGGER.debug("OnScriptRunnerEvent: %s", event) prev_state = self._state if event == ScriptRunnerEvent.SCRIPT_STARTED: if self._state != AppSessionState.SHUTDOWN_REQUESTED: self._state = AppSessionState.APP_IS_RUNNING self._clear_queue() self._enqueue_new_session_message() elif (event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR): if self._state != AppSessionState.SHUTDOWN_REQUESTED: self._state = AppSessionState.APP_NOT_RUNNING script_succeeded = event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS self._enqueue_script_finished_message( ForwardMsg.FINISHED_SUCCESSFULLY if script_succeeded else ForwardMsg.FINISHED_WITH_COMPILE_ERROR) if script_succeeded: # When a script completes successfully, we update our # LocalSourcesWatcher to account for any source code changes # that change which modules should be watched. (This is run on # the main thread, because LocalSourcesWatcher is not # thread safe.) self._ioloop.spawn_callback( self._local_sources_watcher.update_watched_modules) else: assert ( exception is not None ), "exception must be set for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event" msg = ForwardMsg() exception_utils.marshall( msg.session_event.script_compilation_exception, exception) self.enqueue(msg) elif event == ScriptRunnerEvent.SHUTDOWN: # When ScriptRunner shuts down, update our local reference to it, # and check to see if we need to spawn a new one. (This is run on # the main thread.) assert ( client_state is not None), "client_state must be set for the SHUTDOWN event" if self._state == AppSessionState.SHUTDOWN_REQUESTED: # Only clear media files if the script is done running AND the # session is actually shutting down. in_memory_file_manager.clear_session_files(self.id) def on_shutdown(): # We assert above that this is non-null self._client_state = cast(ClientState, client_state) self._scriptrunner = None # Because a new ScriptEvent could have been enqueued while the # scriptrunner was shutting down, we check to see if we should # create a new one. (Otherwise, a newly-enqueued ScriptEvent # won't be processed until another event is enqueued.) self._maybe_create_scriptrunner() self._ioloop.spawn_callback(on_shutdown) # Send a message if our run state changed app_was_running = prev_state == AppSessionState.APP_IS_RUNNING app_is_running = self._state == AppSessionState.APP_IS_RUNNING if app_is_running != app_was_running: self._enqueue_session_state_changed_message()
def _enqueue_file_change_message(self) -> None: LOGGER.debug("Enqueuing script_changed message (id=%s)", self.id) msg = ForwardMsg() msg.session_event.script_changed_on_disk = True self.enqueue(msg)
def _create_script_finished_msg(status) -> ForwardMsg: msg = ForwardMsg() msg.script_finished = status return msg
def _enqueue_session_state_changed_message(self): msg = ForwardMsg() msg.session_state_changed.run_on_save = self._run_on_save msg.session_state_changed.report_is_running = ( self._state == ReportSessionState.REPORT_IS_RUNNING) self.enqueue(msg)
def _create_exception_message(self, e: BaseException) -> ForwardMsg: """Create and return an Exception ForwardMsg.""" msg = ForwardMsg() exception_utils.marshall(msg.delta.new_element.exception, e) return msg
import copy import unittest from typing import Tuple from parameterized import parameterized from streamlit import RootContainer from streamlit.cursor import make_delta_path from streamlit.forward_msg_queue import ForwardMsgQueue from streamlit.elements import legacy_data_frame from streamlit.proto.ForwardMsg_pb2 import ForwardMsg # For the messages below, we don't really care about their contents so much as # their general type. NEW_SESSION_MSG = ForwardMsg() NEW_SESSION_MSG.new_session.config.allow_run_on_save = True TEXT_DELTA_MSG1 = ForwardMsg() TEXT_DELTA_MSG1.delta.new_element.text.body = "text1" TEXT_DELTA_MSG1.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) TEXT_DELTA_MSG2 = ForwardMsg() TEXT_DELTA_MSG2.delta.new_element.text.body = "text2" TEXT_DELTA_MSG2.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) ADD_BLOCK_MSG = ForwardMsg() ADD_BLOCK_MSG.metadata.delta_path[:] = make_delta_path(RootContainer.MAIN, (), 0)
def _create_file_change_message(self) -> ForwardMsg: """Create and return a 'script_changed_on_disk' ForwardMsg.""" msg = ForwardMsg() msg.session_event.script_changed_on_disk = True return msg
def _handle_scriptrunner_event_on_main_thread( self, sender: Optional[ScriptRunner], event: ScriptRunnerEvent, forward_msg: Optional[ForwardMsg] = None, exception: Optional[BaseException] = None, client_state: Optional[ClientState] = None, ) -> None: """Handle a ScriptRunner event. This function must only be called on the main thread. Parameters ---------- sender : ScriptRunner | None The ScriptRunner that emitted the event. (This may be set to None when called from `handle_backmsg_exception`, if no ScriptRunner was active when the backmsg exception was raised.) event : ScriptRunnerEvent The event type. forward_msg : ForwardMsg | None The ForwardMsg to send to the frontend. Set only for the ENQUEUE_FORWARD_MSG event. exception : BaseException | None An exception thrown during compilation. Set only for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event. client_state : streamlit.proto.ClientState_pb2.ClientState | None The ScriptRunner's final ClientState. Set only for the SHUTDOWN event. """ assert (threading.main_thread() == threading.current_thread() ), "This function must only be called on the main thread" if sender is not self._scriptrunner: # This event was sent by a non-current ScriptRunner; ignore it. # This can happen after sppinng up a new ScriptRunner (to handle a # rerun request, for example) while another ScriptRunner is still # shutting down. The shutting-down ScriptRunner may still # emit events. LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event) return prev_state = self._state if event == ScriptRunnerEvent.SCRIPT_STARTED: if self._state != AppSessionState.SHUTDOWN_REQUESTED: self._state = AppSessionState.APP_IS_RUNNING self._clear_queue() self._enqueue_forward_msg(self._create_new_session_message()) elif (event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR): if self._state != AppSessionState.SHUTDOWN_REQUESTED: self._state = AppSessionState.APP_NOT_RUNNING script_succeeded = event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS script_finished_msg = self._create_script_finished_message( ForwardMsg.FINISHED_SUCCESSFULLY if script_succeeded else ForwardMsg.FINISHED_WITH_COMPILE_ERROR) self._enqueue_forward_msg(script_finished_msg) if script_succeeded: # The script completed successfully: update our # LocalSourcesWatcher to account for any source code changes # that change which modules should be watched. self._local_sources_watcher.update_watched_modules() else: # The script didn't complete successfully: send the exception # to the frontend. assert ( exception is not None ), "exception must be set for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event" msg = ForwardMsg() exception_utils.marshall( msg.session_event.script_compilation_exception, exception) self._enqueue_forward_msg(msg) elif event == ScriptRunnerEvent.SHUTDOWN: assert ( client_state is not None), "client_state must be set for the SHUTDOWN event" if self._state == AppSessionState.SHUTDOWN_REQUESTED: # Only clear media files if the script is done running AND the # session is actually shutting down. in_memory_file_manager.clear_session_files(self.id) self._client_state = client_state self._scriptrunner = None elif event == ScriptRunnerEvent.ENQUEUE_FORWARD_MSG: assert ( forward_msg is not None), "null forward_msg in ENQUEUE_FORWARD_MSG event" self._enqueue_forward_msg(forward_msg) # Send a message if our run state changed app_was_running = prev_state == AppSessionState.APP_IS_RUNNING app_is_running = self._state == AppSessionState.APP_IS_RUNNING if app_is_running != app_was_running: self._enqueue_forward_msg( self._create_session_state_changed_message())
def progress(percent): progress_msg = ForwardMsg() progress_msg.upload_report_progress = percent yield ws.write_message(progress_msg.SerializeToString(), binary=True)
def read_forward_msg(self, ws_client): """Parse the next message from a Websocket client into a ForwardMsg.""" data = yield ws_client.read_message() message = ForwardMsg() message.ParseFromString(data) raise gen.Return(message)
"""Unit tests for session_data.py.""" from unittest.mock import patch import copy import unittest from parameterized import parameterized from streamlit import config, RootContainer from streamlit.cursor import make_delta_path from streamlit.session_data import get_url from streamlit.proto.ForwardMsg_pb2 import ForwardMsg from streamlit.proto.Empty_pb2 import Empty as EmptyProto from tests import testutil NEW_SESSION_MSG = ForwardMsg() NEW_SESSION_MSG.metadata.delta_path[:] = make_delta_path(RootContainer.MAIN, (), 0) TEXT_DELTA_MSG = ForwardMsg() TEXT_DELTA_MSG.delta.new_element.text.body = "text1" TEXT_DELTA_MSG.metadata.delta_path[:] = make_delta_path(RootContainer.MAIN, (), 0) EMPTY_DELTA_MSG = ForwardMsg() EMPTY_DELTA_MSG.delta.new_element.empty.CopyFrom(EmptyProto()) EMPTY_DELTA_MSG.metadata.delta_path[:] = make_delta_path(RootContainer.MAIN, (), 0) def _enqueue(report, msg): msg = copy.deepcopy(msg) msg.metadata.delta_path[-1] = len(list(report._master_queue)) report.enqueue(msg)
# See the License for the specific language governing permissions and # limitations under the License. """Unit test of ReportQueue.py.""" import copy import unittest from streamlit.ReportQueue import ReportQueue from streamlit.elements import data_frame_proto from streamlit.proto.BlockPath_pb2 import BlockPath from streamlit.proto.ForwardMsg_pb2 import ForwardMsg # For the messages below, we don't really care about their contents so much as # their general type. INIT_MSG = ForwardMsg() INIT_MSG.initialize.config.sharing_enabled = True TEXT_DELTA_MSG1 = ForwardMsg() TEXT_DELTA_MSG1.delta.new_element.text.body = "text1" TEXT_DELTA_MSG2 = ForwardMsg() TEXT_DELTA_MSG2.delta.new_element.text.body = "text2" DF_DELTA_MSG = ForwardMsg() data_frame_proto.marshall_data_frame({ "col1": [0, 1, 2], "col2": [10, 11, 12] }, DF_DELTA_MSG.delta.new_element.data_frame) ADD_ROWS_MSG = ForwardMsg()
from unittest.mock import patch import copy import unittest from parameterized import parameterized from streamlit import config, RootContainer from streamlit.cursor import make_delta_path from streamlit.report import Report from streamlit.proto.ForwardMsg_pb2 import ForwardMsg from streamlit.proto.StaticManifest_pb2 import StaticManifest from streamlit.proto.Empty_pb2 import Empty as EmptyProto from tests import testutil NEW_REPORT_MSG = ForwardMsg() NEW_REPORT_MSG.new_report.initialize.config.sharing_enabled = True NEW_REPORT_MSG.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) TEXT_DELTA_MSG = ForwardMsg() TEXT_DELTA_MSG.delta.new_element.text.body = "text1" TEXT_DELTA_MSG.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) EMPTY_DELTA_MSG = ForwardMsg() EMPTY_DELTA_MSG.delta.new_element.empty.CopyFrom(EmptyProto()) EMPTY_DELTA_MSG.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0)
def _enqueue_session_state_changed_message(self) -> None: msg = ForwardMsg() msg.session_state_changed.run_on_save = self._run_on_save msg.session_state_changed.script_is_running = ( self._state == AppSessionState.APP_IS_RUNNING) self.enqueue(msg)
"""Unit tests for report.py.""" from unittest.mock import patch import copy import unittest from parameterized import parameterized from streamlit import config, RootContainer from streamlit.cursor import make_delta_path from streamlit.report import Report from streamlit.proto.ForwardMsg_pb2 import ForwardMsg from streamlit.proto.StaticManifest_pb2 import StaticManifest from tests import testutil INIT_MSG = ForwardMsg() INIT_MSG.initialize.config.sharing_enabled = True INIT_MSG.metadata.delta_path[:] = make_delta_path(RootContainer.MAIN, (), 0) TEXT_DELTA_MSG = ForwardMsg() TEXT_DELTA_MSG.delta.new_element.text.body = "text1" TEXT_DELTA_MSG.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) EMPTY_DELTA_MSG = ForwardMsg() EMPTY_DELTA_MSG.delta.new_element.empty.unused = True EMPTY_DELTA_MSG.metadata.delta_path[:] = make_delta_path( RootContainer.MAIN, (), 0) def _enqueue(report, msg):
def _enqueue_script_finished_message( self, status: "ForwardMsg.ScriptFinishedStatus.ValueType") -> None: """Enqueue a script_finished ForwardMsg.""" msg = ForwardMsg() msg.script_finished = status self.enqueue(msg)
def _parse_msg(msg_string): """Parse a ForwardMsg from a string""" msg = ForwardMsg() msg.ParseFromString(msg_string) return msg
def _create_report_finished_msg(status) -> ForwardMsg: msg = ForwardMsg() msg.report_finished = status return msg
def _enqueue_report_finished_message(self): msg = ForwardMsg() msg.report_finished = True self.enqueue(msg)