class SessionData: """ Contains parameters related to running a script, and also houses the ForwardMsgQueue that is used to deliver messages to a connected browser. """ script_path: str script_folder: str name: str command_line: str script_run_id: str _browser_queue: ForwardMsgQueue def __init__(self, script_path: str, command_line: str): """Constructor. Parameters ---------- script_path : str Path of the Python file from which this app is generated. command_line : string Command line as input by the user """ basename = os.path.basename(script_path) self.script_path = os.path.abspath(script_path) self.script_folder = os.path.dirname(self.script_path) self.name = str(os.path.splitext(basename)[0]) # The browser queue contains messages that haven't yet been # delivered to the browser. Periodically, the server flushes # this queue and delivers its contents to the browser. self._browser_queue = ForwardMsgQueue() self.script_run_id = generate_new_id() self.command_line = command_line def enqueue(self, msg): self._browser_queue.enqueue(msg) def clear(self): self._browser_queue.clear() def flush_browser_queue(self): """Clears our browser queue and returns the messages it contained. The Server calls this periodically to deliver new messages to the browser associated with this session. Returns ------- list[ForwardMsg] The messages that were removed from the queue and should be delivered to the browser. """ return self._browser_queue.flush()
class DeltaGeneratorTestCase(unittest.TestCase): def setUp(self, override_root=True): self.forward_msg_queue = ForwardMsgQueue() self.override_root = override_root self.orig_report_ctx = None self.new_script_run_ctx = ScriptRunContext( session_id="test session id", enqueue=self.forward_msg_queue.enqueue, query_string="", session_state=SessionState(), uploaded_file_mgr=UploadedFileManager(), ) if self.override_root: self.orig_report_ctx = get_script_run_ctx() add_script_run_ctx(threading.current_thread(), self.new_script_run_ctx) self.app_session = FakeAppSession() def tearDown(self): self.clear_queue() if self.override_root: add_script_run_ctx(threading.current_thread(), self.orig_report_ctx) def get_message_from_queue(self, index=-1) -> ForwardMsg: """Get a ForwardMsg proto from the queue, by index.""" return self.forward_msg_queue._queue[index] def get_delta_from_queue(self, index=-1) -> Delta: """Get a Delta proto from the queue, by index.""" deltas = self.get_all_deltas_from_queue() return deltas[index] def get_all_deltas_from_queue(self) -> List[Delta]: """Return all the delta messages in our ForwardMsgQueue""" return [ msg.delta for msg in self.forward_msg_queue._queue if msg.HasField("delta") ] def clear_queue(self) -> None: self.forward_msg_queue.clear()
class TestScriptRunner(ScriptRunner): """Subclasses ScriptRunner to provide some testing features.""" def __init__(self, script_name): """Initializes the ScriptRunner for the given script_name""" # DeltaGenerator deltas will be enqueued into self.forward_msg_queue. self.forward_msg_queue = ForwardMsgQueue() self.script_request_queue = ScriptRequestQueue() main_script_path = os.path.join(os.path.dirname(__file__), "test_data", script_name) super(TestScriptRunner, self).__init__( session_id="test session id", session_data=SessionData(main_script_path, "test command line"), enqueue_forward_msg=self.forward_msg_queue.enqueue, client_state=ClientState(), session_state=SessionState(), request_queue=self.script_request_queue, uploaded_file_mgr=UploadedFileManager(), ) # Accumulates uncaught exceptions thrown by our run thread. self.script_thread_exceptions = [] # Accumulates all ScriptRunnerEvents emitted by us. self.events: List[ScriptRunnerEvent] = [] self.event_data: List[Any] = [] def record_event(sender: Optional[ScriptRunner], event: ScriptRunnerEvent, **kwargs): # Assert that we're not getting unexpected `sender` params # from ScriptRunner.on_event assert (sender is None or sender == self), "Unexpected ScriptRunnerEvent sender!" self.events.append(event) self.event_data.append(kwargs) self.on_event.connect(record_event, weak=False) def enqueue_rerun(self, argv=None, widget_states=None, query_string=""): self.script_request_queue.enqueue( ScriptRequest.RERUN, RerunData(widget_states=widget_states, query_string=query_string), ) def enqueue_stop(self): self.script_request_queue.enqueue(ScriptRequest.STOP) def enqueue_shutdown(self): self.script_request_queue.enqueue(ScriptRequest.SHUTDOWN) def _run_script_thread(self): try: super()._run_script_thread() except BaseException as e: self.script_thread_exceptions.append(e) def _run_script(self, rerun_data): self.forward_msg_queue.clear() super()._run_script(rerun_data) def join(self): """Joins the run thread, if it was started""" if self._script_thread is not None: self._script_thread.join() def clear_deltas(self): """Clear all delta messages from our ForwardMsgQueue""" self.forward_msg_queue.clear() def deltas(self) -> List[Delta]: """Return the delta messages in our ForwardMsgQueue""" return [ msg.delta for msg in self.forward_msg_queue._queue if msg.HasField("delta") ] def elements(self) -> List[Element]: """Return the delta.new_element messages in our ForwardMsgQueue.""" return [delta.new_element for delta in self.deltas()] def text_deltas(self) -> List[str]: """Return the string contents of text deltas in our ForwardMsgQueue""" return [ element.text.body for element in self.elements() if element.WhichOneof("type") == "text" ] def get_widget_id(self, widget_type, label): """Returns the id of the widget with the specified type and label""" for delta in self.deltas(): new_element = getattr(delta, "new_element", None) widget = getattr(new_element, widget_type, None) widget_label = getattr(widget, "label", None) if widget_label == label: return widget.id return None