def handle_shell_output()

in gui/backend/gui_plugin/shell/ShellModuleSession.py [0:0]


    def handle_shell_output(self):
        # Read characters from the shell stdout and build responses
        # to deliver to the handle_frontend_command method
        reply_line = ""
        error_buffer = ""
        while not self._shell_exited:
            reply_json = None
            char = self._shell.stdout.read(1)

            if len(char) == 0:
                break

            if not char == '\n':
                reply_line += char
                continue
            # when running on windows, remove the \r (from \r\n sequence)
            if reply_line.endswith('\r'):
                reply_line = reply_line[:-1]

            if reply_line.startswith("["):
                reply_line = f"{{ \"rows\": {reply_line} }}"

            if reply_line.startswith("{"):
                reply_json = json.loads(reply_line)

                # While in python mode, the python engine produces errors by
                # calling the print callback lots of times, to avoid sending a
                # lot of replies to the frontend we will cache consecutive errors
                # and send them in one call to the frontend as soon as a non
                # error response is received from the shell
                if 'error' in reply_json and isinstance(reply_json['error'], str):
                    error_buffer += reply_json["error"]
                else:
                    if len(error_buffer) > 0:
                        self._pending_request.send_output(
                            {"error": error_buffer})
                        error_buffer = ""

                    if 'prompt_descriptor' in reply_json:
                        # remove empty strings
                        reply_json = remove_dict_useless_items(reply_json)

                        # command complete
                        if not self._initialize_complete.is_set():
                            data = {"last_prompt": self._last_prompt,
                                    "module_session_id": self.module_session_id}
                            if self._last_prompt != reply_json:
                                data.update(reply_json)
                            self._pending_request.complete(message="New Shell Interactive session created successfully.",
                                                           data=data)
                            self._initialize_complete.set()
                        else:
                            self._pending_request.complete(
                                data=None if self._last_prompt == reply_json else reply_json)
                            self._command_complete.set()
                        self._last_prompt = reply_json
                    elif 'prompt' in reply_json:
                        # request for a client prompt
                        prompt_event = threading.Event()

                        if 'type' in reply_json and reply_json['type'] == 'password':
                            logger.add_filter({
                                "type": "key",
                                "key": "reply",
                                "expire": Filtering.FilterExpire.OnUse
                            })

                        reply_json.update(
                            {"module_session_id": self.module_session_id})
                        self.send_prompt_response(
                            self._pending_request.task_id, reply_json, lambda: prompt_event.set())

                        # Locks until the prompt is handled
                        prompt_event.wait()

                        if self._prompt_replied:
                            self._shell.stdin.write(self._prompt_reply + "\n")
                            self._shell.stdin.flush()
                        else:
                            self.kill_command()

                    elif 'value' in reply_json:
                        # generic response to send to the client
                        send_response = True
                        if isinstance(reply_json['value'], str) and reply_json['value'].endswith('\r'):
                            reply_json['value'] = reply_json['value'][:-1]
                            if len(reply_json['value']) == 0:
                                send_response = False

                        if send_response:
                            self._pending_request.send_output(reply_json)
                    else:
                        # Shell commands are stored as JSON and sent to the Shell
                        # Then the shell will print them as JSON because of interactive=full
                        # We do not need to reply back the original command to the frontend
                        if self._pending_request.command != reply_json:
                            if 'complete' in self._pending_request.command:
                                self._pending_request.send_output(
                                    reply_json['info'])
                            else:
                                self._pending_request.send_output(reply_json)
            elif reply_line == "Bye!":
                self._shell_exited = True
            else:
                # Some shell errors are not reported as JSON, i.e. initialization errors
                error_buffer += reply_line

            reply_line = ''

        # A pending request is expected to be present in 3 cases:
        # - When the initialization of the session failed
        # - When a CLI call was done
        # - When the Shell session is closed
        if not self._pending_request is None:
            exit_status = None
            attempts = 3
            while exit_status is None and attempts > 0:
                try:
                    exit_status = self._shell.wait(5)
                except subprocess.TimeoutExpired:
                    attempts = attempts - 1

            if exit_status != 0:
                self._pending_request.fail(message=error_buffer, data={
                                           "exit_status": exit_status})
            else:
                data = {"module_session_id": self.module_session_id,
                        "exit_status": exit_status}
                self._pending_request.complete(
                    data=data)

            self._command_complete.set()

            # Finally closes the module session if not already being closed
            if not isinstance(self._pending_request, ShellQuitTask):
                self.close()

        # On error conditions no processing will take place, still the frontend handler thread is waiting, we need to let it go
        self._shell_exited = True
        if not self._initialize_complete.is_set():
            self._initialize_complete.set()