Esempio n. 1
0
    def _handle_toplevel_response(self, msg: ToplevelResponse) -> None:
        if msg.get("error"):
            self._insert_text_directly(msg["error"] + "\n",
                                       ("toplevel", "stderr"))
            self._ensure_visible()

        if "user_exception" in msg:
            self._show_user_exception(msg["user_exception"])
            self._ensure_visible()

        welcome_text = msg.get("welcome_text")
        if welcome_text and welcome_text != self._last_welcome_text:
            self._insert_text_directly(welcome_text, ("comment", ))
            self._last_welcome_text = welcome_text

        if "value_info" in msg:
            num_stripped_question_marks = getattr(
                msg, "num_stripped_question_marks", 0)
            if num_stripped_question_marks > 0:
                # show the value in object inspector
                get_workbench().event_generate("ObjectSelect",
                                               object_id=msg["value_info"].id)
            else:
                # show the value in shell
                value_repr = shorten_repr(msg["value_info"].repr, 10000)
                if value_repr != "None":
                    if get_workbench().in_heap_mode():
                        value_repr = memory.format_object_id(
                            msg["value_info"].id)
                    object_tag = "object_" + str(msg["value_info"].id)
                    self._insert_text_directly(
                        value_repr + "\n", ("toplevel", "value", object_tag))
                    if running_on_mac_os():
                        sequence = "<Command-Button-1>"
                    else:
                        sequence = "<Control-Button-1>"
                    self.tag_bind(
                        object_tag,
                        sequence,
                        lambda _: get_workbench().event_generate(
                            "ObjectSelect", object_id=msg["value_info"].id),
                    )

                    self.active_object_tags.add(object_tag)

        self.mark_set("output_end", self.index("end-1c"))
        self._discard_old_content()
        self._update_visible_io(None)
        self._reset_ansi_attributes()
        self._io_cursor_offset = 0
        self._insert_prompt()
        self._try_submit_input(
        )  # Trying to submit leftover code (eg. second magic command)
        self.see("end")
Esempio n. 2
0
    def handle_toplevel_response(self, msg: ToplevelResponse) -> None:
        # Can be called by event system or by Workbench
        # (if Assistant wasn't created yet but an error came)
        if not msg.get("user_exception") and msg.get("command_name") in [
                "execute_system_command",
                "execute_source",
        ]:
            # Shell commands may be used to investigate the problem, don't clear assistance
            return

        self._clear()

        from thonny.plugins.cpython import CPythonProxy

        if not isinstance(get_runner().get_backend_proxy(), CPythonProxy):
            # TODO: add some support for MicroPython as well
            return

        # prepare for snapshot
        key = msg.get("filename", "<pyshell>")
        self._current_snapshot = {
            "timestamp": datetime.datetime.now().isoformat()[:19],
            "main_file_path": key,
        }
        self._snapshots_per_main_file.setdefault(key, [])
        self._snapshots_per_main_file[key].append(self._current_snapshot)

        if msg.get("user_exception"):
            if not msg["user_exception"].get("message", None):
                msg["user_exception"]["message"] = "<no message>"

            self._exception_info = msg["user_exception"]
            self._explain_exception(msg["user_exception"])
            if get_workbench().get_option(
                    "assistance.open_assistant_on_errors"):
                get_workbench().show_view("AssistantView", set_focus=False)
        else:
            self._exception_info = None

        if msg.get("filename") and os.path.exists(msg["filename"]):
            self.main_file_path = msg["filename"]
            source = read_source(msg["filename"])
            self._start_program_analyses(
                msg["filename"], source,
                _get_imported_user_files(msg["filename"], source))
        else:
            self.main_file_path = None
            self._present_conclusion()
Esempio n. 3
0
    def _execute_file(self, cmd, executor_class):
        self._check_update_tty_mode(cmd)

        if len(cmd.args) >= 1:
            sys.argv = cmd.args
            filename = cmd.args[0]
            if os.path.isabs(filename):
                full_filename = filename
            else:
                full_filename = os.path.abspath(filename)

            with tokenize.open(full_filename) as fp:
                source = fp.read()

            for preproc in self._source_preprocessors:
                source = preproc(source, cmd)

            result_attributes = self._execute_source(source, full_filename,
                                                     "exec", executor_class,
                                                     cmd,
                                                     self._ast_postprocessors)
            result_attributes["filename"] = full_filename
            return ToplevelResponse(command_name=cmd.name, **result_attributes)
        else:
            raise UserError("Command '%s' takes at least one argument" %
                            cmd.name)
Esempio n. 4
0
    def _send_ready_message(self):
        args = dict(cwd=self._cwd)
        # if not clean, then welcome text is already printed as output from the last session
        if not self._welcome_text_printed:
            args["welcome_text"] = self._welcome_text

        self.send_message(ToplevelResponse(**args))
Esempio n. 5
0
 def mainloop(self):
     while True:
         try:
             cmd = self._fetch_command()
             if isinstance(cmd, InputSubmission):
                 raise AssertionError("InputSubmission not supported")
             elif isinstance(cmd, EOFCommand):
                 self.send_message(ToplevelResponse(SystemExit=True))
                 sys.exit()
             else:
                 self.handle_command(cmd)
         except KeyboardInterrupt:
             # Interrupt must always result in waiting_toplevel_command state
             # Don't show error messages, as the interrupted command may have been InlineCommand
             # (handlers of ToplevelCommands in normal cases catch the interrupt and provide
             # relevant message)
             self.send_message(ToplevelResponse())
Esempio n. 6
0
        def create_error_response(**kw):
            if "error" not in kw:
                kw["error"] = traceback.format_exc()

            if isinstance(cmd, ToplevelCommand):
                return ToplevelResponse(command_name=cmd.name, **kw)
            else:
                return InlineResponse(command_name=cmd.name, **kw)
Esempio n. 7
0
 def _cmd_Reset(self, cmd):
     if len(cmd.args) == 0:
         # nothing to do, because Reset always happens in fresh process
         return ToplevelResponse(
             command_name="Reset",
             welcome_text="Python " + _get_python_version_string(),
             executable=sys.executable,
         )
     else:
         raise UserError("Command 'Reset' doesn't take arguments")
Esempio n. 8
0
 def _soft_reboot(self):
     # Need to go to normal mode. MP doesn't run user code in raw mode
     # (CP does, but it doesn't hurt to do it there as well)
     self._connection.write(NORMAL_MODE_CMD)
     self._raw_prompt_ensured = False
     self._connection.read_until(NORMAL_PROMPT)
     self._connection.write(SOFT_REBOOT_CMD)
     self._forward_output_until_active_prompt(self._send_output)
     self._ensure_raw_prompt()
     self.send_message(ToplevelResponse(cwd=self._cwd))
Esempio n. 9
0
 def _cmd_cd(self, cmd):
     if len(cmd.args) == 1:
         path = cmd.args[0]
         try:
             os.chdir(path)
             return ToplevelResponse()
         except FileNotFoundError:
             raise UserError("No such folder: " + path)
         except OSError as e:
             raise UserError(str(e))
     else:
         raise UserError("cd takes one parameter")
Esempio n. 10
0
 def _soft_reboot_for_restarting_user_program(self):
     # Need to go to normal mode. MP doesn't run user code in raw mode
     # (CP does, but it doesn't hurt to do it there as well)
     self._write(NORMAL_MODE_CMD)
     self._connection.read_until(NORMAL_PROMPT)
     self._write(SOFT_REBOOT_CMD)
     self._check_reconnect()
     self._forward_output_until_active_prompt(self._send_output)
     logger.debug("Restoring helpers")
     self._prepare_helpers()
     self._update_cwd()
     self.send_message(ToplevelResponse(cwd=self._cwd))
Esempio n. 11
0
 def _cmd_cd(self, cmd):
     if len(cmd.args) == 1:
         path = cmd.args[0]
         try:
             os.chdir(path)
             return ToplevelResponse()
         except FileNotFoundError:
             raise UserError("No such folder: " + path)
         except OSError as e:
             raise UserError("\n".join(
                 traceback.format_exception_only(type(e), e)))
     else:
         raise UserError("cd takes one parameter")
Esempio n. 12
0
    def _cmd_execute_source(self, cmd):
        """Executes Python source entered into shell"""
        self._check_update_tty_mode(cmd)
        filename = "<pyshell>"
        ws_stripped_source = cmd.source.strip()
        source = ws_stripped_source.strip("?")
        num_stripped_question_marks = len(ws_stripped_source) - len(source)

        # let's see if it's single expression or something more complex
        try:
            root = ast.parse(source, filename=filename, mode="exec")
        except SyntaxError as e:
            error = "".join(traceback.format_exception_only(type(e), e))
            sys.stderr.write(error)
            return ToplevelResponse()

        assert isinstance(root, ast.Module)

        if len(root.body) == 1 and isinstance(root.body[0], ast.Expr):
            mode = "eval"
        elif len(root.body) > 1 and isinstance(root.body[-1], ast.Expr):
            mode = "exec+eval"
        else:
            mode = "exec"

        result_attributes = self._execute_source(
            source,
            filename,
            mode,
            NiceTracer if getattr(cmd, "debug_mode", False) else SimpleRunner,
            cmd,
        )

        result_attributes[
            "num_stripped_question_marks"] = num_stripped_question_marks

        return ToplevelResponse(command_name="execute_source",
                                **result_attributes)
Esempio n. 13
0
    def _cmd_get_environment_info(self, cmd):

        return ToplevelResponse(
            main_dir=self._main_dir,
            path=sys.path,
            usersitepackages=None,
            prefix=None,
            welcome_text="MicroPython " + _get_python_version_string(),
            executable=sys.executable,
            exe_dirs=[],
            in_venv=False,
            python_version=_get_python_version_string(),
            cwd=os.getcwd(),
        )
Esempio n. 14
0
    def _soft_reboot(self, side_command):
        if side_command:
            self._interrupt_to_raw_prompt()

        # Need to go to normal mode. MP doesn't run user code in raw mode
        # (CP does, but it doesn't hurt to do it there as well)
        self._connection.write(NORMAL_MODE_CMD)
        self._connection.read_until(NORMAL_PROMPT)

        self._connection.write(SOFT_REBOOT_CMD)

        if not side_command:
            self._process_until_raw_prompt()
            self.send_message(ToplevelResponse(cwd=self._cwd))
Esempio n. 15
0
 def _cmd_get_environment_info(self, cmd):
     return ToplevelResponse(
         main_dir=self._main_dir,
         sys_path=sys.path,
         usersitepackages=site.getusersitepackages()
         if site.ENABLE_USER_SITE else None,
         prefix=sys.prefix,
         welcome_text="Python " + get_python_version_string(),
         executable=sys.executable,
         exe_dirs=get_exe_dirs(),
         in_venv=(hasattr(sys, "base_prefix")
                  and sys.base_prefix != sys.prefix
                  or hasattr(sys, "real_prefix")
                  and getattr(sys, "real_prefix") != sys.prefix),
         python_version=get_python_version_string(),
         cwd=os.getcwd(),
     )
Esempio n. 16
0
    def handle_command(self, cmd):
        assert isinstance(cmd, (ToplevelCommand, InlineCommand))

        def create_error_response(**kw):
            if isinstance(cmd, ToplevelCommand):
                return ToplevelResponse(command_name=cmd.name, **kw)
            else:
                return InlineResponse(command_name=cmd.name, **kw)

        handler = getattr(self, "_cmd_" + cmd.name, None)

        if handler is None:
            response = create_error_response(error="Unknown command: " +
                                             cmd.name)
        else:
            try:
                response = handler(cmd)
            except SystemExit:
                # Must be caused by Thonny or plugins code
                if isinstance(cmd, ToplevelCommand):
                    traceback.print_exc()
                response = create_error_response(SystemExit=True)
            except UserError as e:
                sys.stderr.write(str(e) + "\n")
                response = create_error_response()
            except KeyboardInterrupt:
                response = create_error_response(
                    user_exception=self._prepare_user_exception())
            except Exception:
                _report_internal_error()
                response = create_error_response(
                    context_info="other unhandled exception")

        if response is False:
            # Command doesn't want to send any response
            return

        elif isinstance(response, dict):
            if isinstance(cmd, ToplevelCommand):
                response = ToplevelResponse(command_name=cmd.name, **response)
            elif isinstance(cmd, InlineCommand):
                response = InlineResponse(cmd.name, **response)

        debug("cmd: " + str(cmd) + ", respin: " + str(response))
        self.send_message(response)
Esempio n. 17
0
    def _prepare_command_response(
            self, response: Union[MessageFromBackend, Dict, None],
            command: CommandToBackend) -> MessageFromBackend:
        if "id" in command and "command_id" not in response:
            response["command_id"] = command["id"]

        if isinstance(response, MessageFromBackend):
            return response
        else:
            if isinstance(response, dict):
                args = response
            else:
                args = {}

            if isinstance(command, ToplevelCommand):
                return ToplevelResponse(command_name=command.name, **args)
            else:
                assert isinstance(command, InlineCommand)
                return InlineResponse(command_name=command.name, **args)
Esempio n. 18
0
 def mainloop(self):
     try:
         while self._should_keep_going():
             try:
                 try:
                     msg = self._incoming_message_queue.get(block=True, timeout=0.01)
                 except queue.Empty:
                     self._perform_idle_tasks()
                 else:
                     if isinstance(msg, InputSubmission):
                         self._handle_user_input(msg)
                     elif isinstance(msg, EOFCommand):
                         self._handle_eof_command(msg)
                     else:
                         self._handle_normal_command(msg)
             except KeyboardInterrupt:
                 self._send_output("KeyboardInterrupt", "stderr")  # CPython idle REPL does this
                 self.send_message(ToplevelResponse())
     except ConnectionClosedException:
         sys.exit(0)
Esempio n. 19
0
    def _forward_unexpected_output(self, stream_name="stdout"):
        "Invoked between commands"
        data = self._connection.read_all(check_error=False)
        if data:
            met_prompt = False
            while data.endswith(NORMAL_PROMPT) or data.endswith(FIRST_RAW_PROMPT):
                # looks like the device was resetted
                met_prompt = True

                if data.endswith(NORMAL_PROMPT):
                    terminator = NORMAL_PROMPT
                else:
                    terminator = FIRST_RAW_PROMPT

                # hide the prompt from the output ...
                data = data[: -len(terminator)]

            self._send_output(data.decode(ENCODING, "replace"), stream_name)
            if met_prompt:
                # ... and recreate Thonny prompt
                self.send_message(ToplevelResponse())
Esempio n. 20
0
    def mainloop(self):
        # Don't use threading for creating a management thread, because I don't want them
        # to be affected by threading.settrace
        _thread.start_new_thread(self._read_incoming_messages, ())

        while self._should_keep_going():
            try:
                try:
                    msg = self._incoming_message_queue.get(block=True,
                                                           timeout=0.01)
                except queue.Empty:
                    self._perform_idle_tasks()
                else:
                    if isinstance(msg, InputSubmission):
                        self._handle_user_input(msg)
                    elif isinstance(msg, EOFCommand):
                        self._handle_eof_command(msg)
                    else:
                        self._handle_normal_command(msg)
            except KeyboardInterrupt:
                self._send_output("KeyboardInterrupt",
                                  "stderr")  # CPython idle REPL does this
                self.send_message(ToplevelResponse())
Esempio n. 21
0
 def _handle_eof_command(self, msg: EOFCommand) -> None:
     self.send_message(ToplevelResponse(SystemExit=True))
     sys.exit()
Esempio n. 22
0
    def handle_command(self, cmd):
        self._report_time("before " + cmd.name)
        assert isinstance(cmd, (ToplevelCommand, InlineCommand))

        if "local_cwd" in cmd:
            self._local_cwd = cmd["local_cwd"]

        def create_error_response(**kw):
            if "error" not in kw:
                kw["error"] = traceback.format_exc()

            if isinstance(cmd, ToplevelCommand):
                return ToplevelResponse(command_name=cmd.name, **kw)
            else:
                return InlineResponse(command_name=cmd.name, **kw)

        handler = getattr(self, "_cmd_" + cmd.name, None)

        if handler is None:
            response = create_error_response(error="Unknown command: " +
                                             cmd.name)
        else:
            try:
                response = handler(cmd)
            except SystemExit:
                # Must be caused by Thonny or plugins code
                if isinstance(cmd, ToplevelCommand):
                    traceback.print_exc()
                response = create_error_response(SystemExit=True)
            except UserError as e:
                sys.stderr.write(str(e) + "\n")
                response = create_error_response()
            except KeyboardInterrupt:
                response = create_error_response(error="Interrupted",
                                                 interrupted=True)
            except ProtocolError as e:
                self._send_output(
                    "THONNY FAILED TO EXECUTE %s (%s)\n" %
                    (cmd.name, e.message), "stderr")
                self._send_output("CAPTURED DATA: %r\n" % e.captured, "stderr")
                self._send_output("TRYING TO RECOVER ...\n", "stderr")
                # TODO: detect when there is no output for long time and suggest interrupt
                self._forward_output_until_active_prompt("stdout")
                response = create_error_response(error=e.message)
            except Exception:
                _report_internal_error()
                response = create_error_response(
                    context_info="other unhandled exception")

        if response is None:
            response = {}

        if response is False:
            # Command doesn't want to send any response
            return

        elif isinstance(response, dict):
            if isinstance(cmd, ToplevelCommand):
                response = ToplevelResponse(command_name=cmd.name, **response)
            elif isinstance(cmd, InlineCommand):
                response = InlineResponse(cmd.name, **response)

        if "id" in cmd and "command_id" not in response:
            response["command_id"] = cmd["id"]

        debug("cmd: " + str(cmd) + ", respin: " + str(response))
        self.send_message(response)

        self._report_time("after " + cmd.name)
Esempio n. 23
0
 def _send_ready_message(self):
     self.send_message(
         ToplevelResponse(welcome_text=self._welcome_text, cwd=self._cwd))
Esempio n. 24
0
    def _handle_normal_command(self, cmd: CommandToBackend) -> None:

        self._report_time("before " + cmd.name)
        assert isinstance(cmd, (ToplevelCommand, InlineCommand))

        if "local_cwd" in cmd:
            self._local_cwd = cmd["local_cwd"]

        def create_error_response(**kw):
            if "error" not in kw:
                kw["error"] = traceback.format_exc()

            if isinstance(cmd, ToplevelCommand):
                return ToplevelResponse(command_name=cmd.name, **kw)
            else:
                return InlineResponse(command_name=cmd.name, **kw)

        handler = getattr(self, "_cmd_" + cmd.name, None)

        if handler is None:
            response = create_error_response(error="Unknown command: " +
                                             cmd.name)
        else:
            try:
                response = handler(cmd)
            except SystemExit:
                # Must be caused by Thonny or plugins code
                if isinstance(cmd, ToplevelCommand):
                    traceback.print_exc()
                response = create_error_response(SystemExit=True)
            except UserError as e:
                sys.stderr.write(str(e) + "\n")
                response = create_error_response()
            except KeyboardInterrupt:
                response = create_error_response(error="Interrupted",
                                                 interrupted=True)
            except ConnectionClosedException as e:
                self._on_connection_closed(e)
            except ManagementError as e:
                if "KeyboardInterrupt" in e.err:
                    response = create_error_response(error="Interrupted",
                                                     interrupted=True)
                else:
                    self._send_output(
                        "THONNY FAILED TO EXECUTE COMMAND %s\n" % cmd.name,
                        "stderr")
                    # traceback.print_exc() # I'll know the trace from command
                    self._show_error("\n")
                    self._show_error("SCRIPT:\n" + e.script + "\n")
                    self._show_error("STDOUT:\n" + e.out + "\n")
                    self._show_error("STDERR:\n" + e.err + "\n")

                    response = create_error_response(error="ManagementError")
            except Exception:
                _report_internal_error()
                response = create_error_response(
                    context_info="other unhandled exception")

        if response is None:
            response = {}

        if response is False:
            # Command doesn't want to send any response
            return

        elif isinstance(response, dict):
            if isinstance(cmd, ToplevelCommand):
                response = ToplevelResponse(command_name=cmd.name, **response)
            elif isinstance(cmd, InlineCommand):
                response = InlineResponse(cmd.name, **response)

        debug("cmd: " + str(cmd) + ", respin: " + str(response))
        self.send_message(self._prepare_command_response(response, cmd))

        self._check_perform_just_in_case_gc()

        self._report_time("after " + cmd.name)
Esempio n. 25
0
 def create_error_response(**kw):
     if isinstance(cmd, ToplevelCommand):
         return ToplevelResponse(command_name=cmd.name, **kw)
     else:
         return InlineResponse(command_name=cmd.name, **kw)
Esempio n. 26
0

class ExecutionError(Exception):
    pass


def _report_internal_error():
    print("PROBLEM WITH THONNY'S BACK-END:\n", file=sys.stderr)
    traceback.print_exc()


if __name__ == "__main__":
    port = None if sys.argv[1] == "None" else sys.argv[1]
    try:
        if port == "webrepl":
            url = sys.argv[2]
            password = sys.argv[3]
            from thonny.plugins.micropython.webrepl_connection import WebReplConnection

            connection = WebReplConnection(url, password)
        else:
            from thonny.plugins.micropython.serial_connection import SerialConnection

            connection = SerialConnection(port, BAUDRATE)
    except ConnectionFailedException as e:
        msg = ToplevelResponse(error=str(e))
        sys.stdout.write(serialize_message(msg) + "\n")
        connection = None

    vm = MicroPythonBackend(connection)
Esempio n. 27
0
    def _send_ready_message(self):
        args = dict(cwd=self._cwd)
        args["welcome_text"] = self._welcome_text

        self.send_message(ToplevelResponse(**args))