예제 #1
0
    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

        self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                            filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        def callback(ok, report):
            # XXX: the ok is not trustable
            if report.has_compilation_error() or report.has_execution_error():
                ok = False

            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report, 'exec')
            self.output_console.see('1.0')

            # Enable or disable the evaluation bar according to the execution status
            if report.has_compilation_error(
            ):  # XXX: only for compilation ? , otherwise:  or report.has_execution_error():
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None
            else:
                self.input_console.focus_set()
                #self.switch_input_status(True)

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
            tracing.send_statement_execute(report,
                                           tr(self.mode),
                                           filename=filename)

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)
예제 #2
0
    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        if not expr:
            return
        tracing.send_statement("started", "evaluation", {
            "https://www.lip6.fr/mocah/invalidURI/extensions/mode":
            tr(self.mode)
        })
        tracing.user_is_interacting()
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                                "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report, 'eval')

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
            tracing.send_statement_evaluate(report,
                                            tr(self.mode),
                                            instruction=expr)

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)
예제 #3
0
    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

        self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                            filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report)

            # Enable or disable the evaluation bar according to the execution status
            if ok:
                self.input_console.focus_set()
                #self.switch_input_status(True)
            else:
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)
예제 #4
0
    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        if not expr:
            return
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                                "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report, 'eval')

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)
예제 #5
0
    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

            
        self.interpreter = InterpreterProxy(self.app.root, self.app.mode, filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False
        
        def callback(ok, report):
            # XXX: the ok is not trustable
            if report.has_compilation_error() or report.has_execution_error():
                ok = False
            
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report, 'exec')
            self.output_console.see('1.0')

            # Enable or disable the evaluation bar according to the execution status
            if report.has_compilation_error(): # XXX: only for compilation ? , otherwise:  or report.has_execution_error():
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None            
            else:
                self.input_console.focus_set()
                #self.switch_input_status(True)

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
                
        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)
예제 #6
0
    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        if not expr:
            return
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode, "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report, 'eval')

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)
예제 #7
0
class Console:
    """
    Interactive console of MrPython, consisting of two widgets : output and input
    """

    from ModifiedColorDelegator import ModifiedColorDelegator
    from ModifiedUndoDelegator import ModifiedUndoDelegator
    from IdleHistory import History

    SHELL_TITLE = "Python " + python_version() + " Shell"
    TEXT_COLORS_BY_MODE = {
        'run': 'green',
        'error': 'red',
        'normal': 'black',
        'warning': 'orange',
        'info': 'blue'
    }

    def __init__(self, output_parent, input_parent, app):
        """
        Create and configure the shell (the text widget that gives informations
        and the interactive shell)
        """
        self.app = app
        # Creating output console
        self.frame_output = Frame(output_parent)
        self.scrollbar = Scrollbar(self.frame_output)
        self.scrollbar.grid(row=0, column=1, sticky=(N, S))

        self.output_console = ReadOnlyText(self.frame_output,
                                           height=15,
                                           yscrollcommand=self.scrollbar.set)

        self.hyperlinks = HyperlinkManager(self.output_console)

        self.frame_output.config(borderwidth=1, relief=GROOVE)
        self.output_console.grid(row=0, column=0, sticky=(N, S, E, W))
        self.scrollbar.config(command=self.output_console.yview)

        self.frame_output.rowconfigure(0, weight=1)
        self.frame_output.columnconfigure(0, weight=1)

        # Creating input console
        self.frame_input = Frame(input_parent)
        self.arrows = Label(self.frame_input, text=" >>> ")
        self.input_console = Entry(
            self.frame_input,
            background='#775F57',
            #height=1,
            state='disabled',
            relief=FLAT)
        self.input_console.bind('<Return>', self.evaluate_action)
        self.input_history = ConsoleHistory()
        self.input_console.bind('<Up>', self.history_up_action)
        self.input_console.bind('<Down>', self.history_down_action)
        #self.frame_input.config(borderwidth=1, relief=GROOVE)
        self.eval_button = Button(self.frame_input,
                                  text="Eval",
                                  command=self.evaluate_action,
                                  width=7,
                                  state='disabled')
        self.arrows.config(borderwidth=1, relief=RIDGE)
        self.arrows.grid(row=0, column=0)
        self.input_console.grid(row=0, column=1, sticky="ew")
        self.eval_button.grid(row=0, column=2)

        self.frame_input.columnconfigure(1, weight=1)

        # Redirect the Python output, input and error stream to the console
        import IOBinding
        self.stdin = PseudoInputFile(self, "error", IOBinding.encoding)
        self.stdout = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.stderr = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.console = PseudoOutputFile(self, "error", IOBinding.encoding)
        #sys.stdout = self.stdout
        #sys.stderr = self.stderr
        #sys.stdin = self.stdin
        # The current Python mode
        self.mode = "student"

        self.reading = False
        self.executing = False
        self.canceled = False
        self.endoffile = False
        self.closing = False
        self._stop_readling_flag = False

        self.history = self.History(self.output_console)
        self.undo = undo = self.ModifiedUndoDelegator()
        self.io = IOBinding.IOBinding(self)
        self.begin()
        self.configure_color_tags()
        self.switch_input_status(True)
        self.interpreter = None

    def change_font(self, nfont):
        self.output_console.configure(font=nfont)
        self.input_console.configure(font=nfont)

    def configure_color_tags(self):
        """ Set the colors for the specific tags """
        self.output_console.tag_config('run', foreground='green')
        self.output_console.tag_config('error', foreground='red')
        self.output_console.tag_config('normal', foreground='black')
        self.output_console.tag_config('warning', foreground='orange')
        self.output_console.tag_config('info', foreground='brown')
        self.output_console.tag_config('stdout', foreground='gray')

    def reset_output(self):
        """ Clear all the output console """
        #self.output_console.config(state=NORMAL)
        self.output_console.delete(1.0, END)
        self.begin()

        self.write("MrPython v.{} -- mode {}\n".format(
            version.version_string(), tr(self.mode)))
        #self.output_console.config(state=DISABLED)

    def change_mode(self, mode):
        """ When the mode change : clear the output console and display
            the new mode """
        self.mode = mode
        self.reset_output()
        self.hyperlinks.reset()
        #self.switch_input_status(False)

    def write_report(self, status, report, exec_mode):
        tag = 'run'
        if not status:
            tag = 'error'

        self.hyperlinks.reset()

        self.write(report.header, tags=(tag))
        #self.write("\n")

        has_convention_error = False

        for error in report.convention_errors:
            if error.severity == "error" and not has_convention_error:
                self.write(tr("-----\nPython101 convention errors:\n-----\n"),
                           tags='info')
                has_convention_error = True

            hyper, hyper_spec = self.hyperlinks.add(ErrorCallback(self, error))
            #print("hyper={}".format(hyper))
            #print("hyper_spec={}".format(hyper_spec))
            self.write("\n")
            self.write(str(error), tags=(error.severity, hyper, hyper_spec))
            self.write("\n")

        if not status:
            has_compilation_error = False
            for error in report.compilation_errors:
                if error.severity == "error" and not has_compilation_error:
                    self.write(tr(
                        "\n-----\nCompilation errors (Python interpreter):\n-----\n"
                    ),
                               tags='info')
                    has_compilation_error = True
                hyper, hyper_spec = self.hyperlinks.add(
                    ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error),
                           tags=(error.severity, hyper, hyper_spec))
                self.write("\n")

            has_execution_error = False
            for error in report.execution_errors:
                if error.severity == "error" and not has_execution_error:
                    self.write(tr(
                        "\n-----\nExecution errors (Python interpreter):\n-----\n"
                    ),
                               tags='info')
                    has_execution_error = True

                hyper, hyper_spec = self.hyperlinks.add(
                    ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error),
                           tags=(error.severity, hyper, hyper_spec))
                self.write("\n")

        else:
            has_execution_error = False
            for error in report.execution_errors:
                if error.severity == "error" and not has_execution_error:
                    self.write(tr(
                        "\n-----\nExecution errors (Python interpreter):\n-----\n"
                    ),
                               tags='info')
                    has_execution_error = True

                hyper, hyper_spec = self.hyperlinks.add(
                    ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error),
                           tags=(error.severity, hyper, hyper_spec))
                self.write("\n")

            self.write(str(report.output), tags=('stdout'))
            if report.result is not None:
                self.write(repr(report.result), tags=('normal'))

        if exec_mode == 'exec' and status and self.mode == tr(
                'student') and report.nb_defined_funs > 0:
            if report.nb_passed_tests > 1:
                self.write("==> " +
                           tr("All the {} tests passed with success").format(
                               report.nb_passed_tests),
                           tags=('run'))
            elif report.nb_passed_tests == 1:
                self.write("==> " + tr(
                    "Only one (successful) test found, it's probably not enough"
                ),
                           tags=('warning'))
                report.add_convention_error(
                    "warning",
                    tr("Missing tests"),
                    details=tr(
                        "Only one (successful) test found, it's probably not enough"
                    ),
                    class_name="OneTestWarning"
                )  # Error added for future tracing
            else:
                self.write("==> " +
                           tr("There is no test! you have to write tests!"),
                           tags=('error'))
                report.add_convention_error(
                    "error",
                    tr("Missing tests"),
                    details=tr("There is no test! you have to write tests!"),
                    class_name="NoTestError")  # Error added for future tracing

            # Check if user identified themselves for tracing
            if exec_mode == 'exec' and self.mode == tr('student'):
                tracing.check_modified_student_number(report.first_line)
                if tracing.student_hash_uninitialized(
                ):  # User is not identified
                    error_message = (
                        "Numéro d'étudiant non initialisé.\n"
                        "Veuillez saisir votre ou vos numéros en premiere ligne au format:\n"
                        "'# numero' ou '# votre-numero binome-numero' \n")
                    self.write("\n==> " + error_message, tags=('warning'))
                    report.add_convention_error(
                        "warning",
                        "Uninitialized student number",
                        details=error_message,
                        class_name="UninitializedStudentNumberWarning")
        self.write(report.footer, tags=(tag))

    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        if not expr:
            return
        tracing.send_statement("started", "evaluation", {
            "https://www.lip6.fr/mocah/invalidURI/extensions/mode":
            tr(self.mode)
        })
        tracing.user_is_interacting()
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                                "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report, 'eval')

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
            tracing.send_statement_evaluate(report,
                                            tr(self.mode),
                                            instruction=expr)

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)

    def history_up_action(self, event=None):
        entry = self.input_history.move_past()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def history_down_action(self, event=None):
        entry = self.input_history.move_future()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def switch_input_status(self, on):
        """ Enable or disable the evaluation bar and button """
        stat = None
        bg = None
        if on:
            stat = 'normal'
            bg = '#FFA500'
        else:
            stat = 'disabled'
            bg = '#775F57'
        self.input_console.config(state=stat, background=bg)
        self.eval_button.config(state=stat)

    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

        self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                            filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        def callback(ok, report):
            # XXX: the ok is not trustable
            if report.has_compilation_error() or report.has_execution_error():
                ok = False

            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report, 'exec')
            self.output_console.see('1.0')

            # Enable or disable the evaluation bar according to the execution status
            if report.has_compilation_error(
            ):  # XXX: only for compilation ? , otherwise:  or report.has_execution_error():
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None
            else:
                self.input_console.focus_set()
                #self.switch_input_status(True)

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
            tracing.send_statement_execute(report,
                                           tr(self.mode),
                                           filename=filename)

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)

    def no_file_to_run_message(self):
        self.reset_output()
        self.write("=== No file to run ===", "error")

    def write(self, s, tags=()):
        """ Write into the output console """
        if isinstance(s, str) and len(s) and max(s) > '\uffff':
            # Tk doesn't support outputting non-BMP characters
            # Let's assume what printed string is not very long,
            # find first non-BMP character and construct informative
            # UnicodeEncodeError exception.
            for start, char in enumerate(s):
                if char > '\uffff':
                    break
            raise UnicodeEncodeError("UCS-2", char, start, start + 1,
                                     'Non-BMP character not supported in Tk')
        try:
            self.output_console.mark_gravity("iomark", "right")
            if isinstance(s, (bytes, bytes)):
                s = s.decode(IOBinding.encoding, "replace")
            #self.output_console.configure(state='normal')
            self.output_console.insert("iomark", s, tags)
            #self.output_console.configure(state='disabled')
            self.output_console.see("iomark")
            self.output_console.update()
            self.output_console.mark_gravity("iomark", "left")
        except:
            raise
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt

    def begin(self):
        """ Display some informations in the output console at the beginning """
        self.output_console.mark_set("iomark", "insert")
        sys.displayhook = rpc.displayhook
        self.write("Python %s on %s\n" % (sys.version, sys.platform))

    def readline(self):
        save = self.READING
        try:
            self.READING = 1
        finally:
            self.READING = save
        if self._stop_readline_flag:
            self._stop_readline_flag = False
            return ""
        line = self.output_console.get("iomark", "end-1c")
        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
            line = "\n"
        self.reset_output()
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt
        if self.endoffile:
            self.endoffile = 0
            line = ""
        return line

    def reset_undo(self):
        self.undo.reset_undo()
예제 #8
0
class Console:
    """
    Interactive console of MrPython, consisting of two widgets : output and input
    """

    from ModifiedColorDelegator import ModifiedColorDelegator
    from ModifiedUndoDelegator import ModifiedUndoDelegator
    from IdleHistory import History

    SHELL_TITLE = "Python " + python_version() + " Shell"
    TEXT_COLORS_BY_MODE = {
        'run': 'green'
        , 'error': 'red'
        , 'normal': 'black'
        , 'warning': 'orange'
        , 'info' : 'blue'    }

    def __init__(self, output_parent, input_parent, app):
        """
        Create and configure the shell (the text widget that gives informations
        and the interactive shell)
        """
        self.app = app
        # Creating output console
        self.frame_output = Frame(output_parent)
        self.scrollbar = Scrollbar(self.frame_output)
        self.scrollbar.grid(row=0, column=1, sticky=(N, S))



        self.output_console = ReadOnlyText(self.frame_output, height=15, 
                                   yscrollcommand=self.scrollbar.set)

        self.hyperlinks = HyperlinkManager(self.output_console)

        self.frame_output.config(borderwidth=1, relief=GROOVE)
        self.output_console.grid(row=0, column=0, sticky=(N, S, E, W))
        self.scrollbar.config(command=self.output_console.yview)

        self.frame_output.rowconfigure(0, weight=1)
        self.frame_output.columnconfigure(0, weight=1)

        # Creating input console
        self.frame_input = Frame(input_parent)
        self.arrows = Label(self.frame_input, text=" >>> ")
        self.input_console = Entry(self.frame_input, background='#775F57',
                                  #height=1,
                                   state='disabled', relief=FLAT)
        self.input_console.bind('<Return>', self.evaluate_action)
        self.input_history = ConsoleHistory()
        self.input_console.bind('<Up>', self.history_up_action)
        self.input_console.bind('<Down>', self.history_down_action)
        #self.frame_input.config(borderwidth=1, relief=GROOVE)
        self.eval_button = Button(self.frame_input, text="Eval",
                                  command=self.evaluate_action, width=7,
                                  state='disabled')
        self.arrows.config(borderwidth=1, relief=RIDGE)
        self.arrows.grid(row=0, column=0)
        self.input_console.grid(row=0, column=1, sticky="ew")
        self.eval_button.grid(row=0, column=2)

        self.frame_input.columnconfigure(1, weight=1)

	# Redirect the Python output, input and error stream to the console
        import IOBinding
        self.stdin = PseudoInputFile(self, "error", IOBinding.encoding)
        self.stdout = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.stderr = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.console = PseudoOutputFile(self, "error", IOBinding.encoding)
        #sys.stdout = self.stdout
        #sys.stderr = self.stderr
        #sys.stdin = self.stdin
        # The current Python mode 
        self.mode = "student"

        self.reading = False
        self.executing = False
        self.canceled = False
        self.endoffile = False
        self.closing = False
        self._stop_readling_flag = False

        self.history = self.History(self.output_console)
        self.undo = undo = self.ModifiedUndoDelegator()
        self.io = IOBinding.IOBinding(self)
        self.begin()
        self.configure_color_tags()
        self.switch_input_status(True)
        self.interpreter = None

    def change_font(self, nfont):
        self.output_console.configure(font=nfont)
        self.input_console.configure(font=nfont)

    def configure_color_tags(self):
        """ Set the colors for the specific tags """
        self.output_console.tag_config('run', foreground='green')
        self.output_console.tag_config('error', foreground='red')
        self.output_console.tag_config('normal', foreground='black')
        self.output_console.tag_config('warning', foreground='orange')
        self.output_console.tag_config('info', foreground='brown')
        self.output_console.tag_config('stdout', foreground='gray')

    def reset_output(self):
        """ Clear all the output console """
        #self.output_console.config(state=NORMAL)
        self.output_console.delete(1.0, END)
        self.begin()

        self.write("MrPython v.{} -- mode {}\n".format(version.version_string(),
                                                       tr(self.mode)))
        #self.output_console.config(state=DISABLED)


    def change_mode(self, mode):
        """ When the mode change : clear the output console and display
            the new mode """
        self.mode = mode
        self.reset_output()
        self.hyperlinks.reset()
        #self.switch_input_status(False)

    def write_report(self, status, report, exec_mode):
        tag = 'run'
        if not status:
            tag = 'error'
            
        self.hyperlinks.reset()

        self.write(report.header, tags=(tag))
        #self.write("\n")
        
        has_convention_error = False
        
        for error in report.convention_errors:
            if error.severity == "error" and not has_convention_error:
                self.write(tr("-----\nPython101 convention errors:\n-----\n"), tags='info')
                has_convention_error = True

            hyper, hyper_spec = self.hyperlinks.add(ErrorCallback(self, error))
            #print("hyper={}".format(hyper))
            #print("hyper_spec={}".format(hyper_spec))
            self.write("\n")
            self.write(str(error), tags=(error.severity, hyper, hyper_spec))
            self.write("\n")

        if not status:
            has_compilation_error = False
            for error in report.compilation_errors:
                if error.severity == "error" and not has_compilation_error:
                    self.write(tr("\n-----\nCompilation errors (Python interpreter):\n-----\n"), tags='info')
                    has_compilation_error = True
                hyper, hyper_spec = self.hyperlinks.add(ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error), tags=(error.severity, hyper, hyper_spec))
                self.write("\n")


            has_execution_error = False
            for error in report.execution_errors:
                if error.severity == "error" and not has_execution_error:
                    self.write(tr("\n-----\nExecution errors (Python interpreter):\n-----\n"), tags='info')
                    has_execution_error = True
                    
                hyper, hyper_spec = self.hyperlinks.add(ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error), tags=(error.severity, hyper, hyper_spec))
                self.write("\n")

        else:
            has_execution_error = False
            for error in report.execution_errors:
                if error.severity == "error" and not has_execution_error:
                    self.write(tr("\n-----\nExecution errors (Python interpreter):\n-----\n"), tags='info')
                    has_execution_error = True

                hyper, hyper_spec = self.hyperlinks.add(ErrorCallback(self, error))
                self.write("\n")
                self.write(str(error), tags=(error.severity, hyper, hyper_spec))
                self.write("\n")


            self.write(str(report.output), tags=('stdout'))
            if report.result is not None:
                self.write(repr(report.result), tags=('normal'))

        if exec_mode == 'exec' and status and self.mode == tr('student') and report.nb_defined_funs > 0:
            if report.nb_passed_tests > 1:
                self.write("==> " + tr("All the {} tests passed with success").format(report.nb_passed_tests), tags=('run'))
            elif report.nb_passed_tests == 1:
                self.write("==> " + tr("Only one (successful) test found, it's probably not enough"), tags=('warning'))
            else:
                self.write("==> " + tr("There is no test! you have to write tests!"), tags=('error'))
        
        self.write(report.footer, tags=(tag))

    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        if not expr:
            return
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode, "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report, 'eval')

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)

    def history_up_action(self, event=None):
        entry = self.input_history.move_past()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def history_down_action(self, event=None):
        entry = self.input_history.move_future()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def switch_input_status(self, on):
        """ Enable or disable the evaluation bar and button """
        stat = None
        bg = None
        if on:
            stat = 'normal'
            bg = '#FFA500'
        else:
            stat = 'disabled'
            bg = '#775F57'
        self.input_console.config(state=stat, background=bg)
        self.eval_button.config(state=stat)
        
    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

            
        self.interpreter = InterpreterProxy(self.app.root, self.app.mode, filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False
        
        def callback(ok, report):
            # XXX: the ok is not trustable
            if report.has_compilation_error() or report.has_execution_error():
                ok = False
            
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report, 'exec')
            self.output_console.see('1.0')

            # Enable or disable the evaluation bar according to the execution status
            if report.has_compilation_error(): # XXX: only for compilation ? , otherwise:  or report.has_execution_error():
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None            
            else:
                self.input_console.focus_set()
                #self.switch_input_status(True)

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None
                
        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)

    def no_file_to_run_message(self):
        self.reset_output()
        self.write("=== No file to run ===", "error")


    def write(self, s, tags=()):
        """ Write into the output console """
        if isinstance(s, str) and len(s) and max(s) > '\uffff':
            # Tk doesn't support outputting non-BMP characters
            # Let's assume what printed string is not very long,
            # find first non-BMP character and construct informative
            # UnicodeEncodeError exception.
            for start, char in enumerate(s):
                if char > '\uffff':
                    break
            raise UnicodeEncodeError("UCS-2", char, start, start+1,
                                     'Non-BMP character not supported in Tk')
        try:
            self.output_console.mark_gravity("iomark", "right")
            if isinstance(s, (bytes, bytes)):
                s = s.decode(IOBinding.encoding, "replace")
            #self.output_console.configure(state='normal')
            self.output_console.insert("iomark", s, tags)
            #self.output_console.configure(state='disabled')
            self.output_console.see("iomark")
            self.output_console.update()
            self.output_console.mark_gravity("iomark", "left")
        except:
            raise
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt


    def begin(self):
        """ Display some informations in the output console at the beginning """
        self.output_console.mark_set("iomark", "insert")
        sys.displayhook = rpc.displayhook
        self.write("Python %s on %s\n" %
                   (sys.version, sys.platform))


    def readline(self):
        save = self.READING
        try:
            self.READING = 1
        finally:
            self.READING = save
        if self._stop_readline_flag:
            self._stop_readline_flag = False
            return ""
        line = self.output_console.get("iomark", "end-1c")
        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
            line = "\n"
        self.reset_output()
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt
        if self.endoffile:
            self.endoffile = 0
            line = ""
        return line


    def reset_undo(self):
        self.undo.reset_undo()
예제 #9
0
class Console:
    """
    Interactive console of MrPython, consisting of two widgets : output and input
    """

    from ModifiedColorDelegator import ModifiedColorDelegator
    from ModifiedUndoDelegator import ModifiedUndoDelegator
    from IdleHistory import History

    SHELL_TITLE = "Python " + python_version() + " Shell"
    TEXT_COLORS_BY_MODE = {
        'run': 'green',
        'error': 'red',
        'normal': 'black',
        'warning': 'orange'
    }

    def __init__(self, output_parent, input_parent, app):
        """
        Create and configure the shell (the text widget that gives informations
        and the interactive shell)
        """
        self.app = app
        # Creating output console
        self.frame_output = Frame(output_parent)
        self.scrollbar = Scrollbar(self.frame_output)
        self.scrollbar.grid(row=0, column=1, sticky=(N, S))

        self.output_console = ReadOnlyText(self.frame_output,
                                           height=15,
                                           yscrollcommand=self.scrollbar.set)

        self.frame_output.config(borderwidth=1, relief=GROOVE)
        self.output_console.grid(row=0, column=0, sticky=(N, S, E, W))
        self.scrollbar.config(command=self.output_console.yview)

        self.frame_output.rowconfigure(0, weight=1)
        self.frame_output.columnconfigure(0, weight=1)

        # Creating input console
        self.frame_input = Frame(input_parent)
        self.arrows = Label(self.frame_input, text=" >>> ")
        self.input_console = Entry(
            self.frame_input,
            background='#775F57',
            #height=1,
            state='disabled',
            relief=FLAT)
        self.input_console.bind('<Return>', self.evaluate_action)
        self.input_history = ConsoleHistory()
        self.input_console.bind('<Up>', self.history_up_action)
        self.input_console.bind('<Down>', self.history_down_action)
        #self.frame_input.config(borderwidth=1, relief=GROOVE)
        self.eval_button = Button(self.frame_input,
                                  text="Eval",
                                  command=self.evaluate_action,
                                  width=7,
                                  state='disabled')
        self.arrows.config(borderwidth=1, relief=RIDGE)
        self.arrows.grid(row=0, column=0)
        self.input_console.grid(row=0, column=1, sticky="ew")
        self.eval_button.grid(row=0, column=2)

        self.frame_input.columnconfigure(1, weight=1)

        # Redirect the Python output, input and error stream to the console
        import IOBinding
        self.stdin = PseudoInputFile(self, "error", IOBinding.encoding)
        self.stdout = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.stderr = PseudoOutputFile(self, "error", IOBinding.encoding)
        self.console = PseudoOutputFile(self, "error", IOBinding.encoding)
        #sys.stdout = self.stdout
        #sys.stderr = self.stderr
        #sys.stdin = self.stdin
        # The current Python mode
        self.mode = "student"

        self.reading = False
        self.executing = False
        self.canceled = False
        self.endoffile = False
        self.closing = False
        self._stop_readling_flag = False

        self.history = self.History(self.output_console)
        self.undo = undo = self.ModifiedUndoDelegator()
        self.io = IOBinding.IOBinding(self)
        self.begin()
        self.configure_color_tags()
        self.switch_input_status(True)
        self.interpreter = None

    def change_font(self, nfont):
        self.output_console.configure(font=nfont)
        self.input_console.configure(font=nfont)

    def configure_color_tags(self):
        """ Set the colors for the specific tags """
        self.output_console.tag_config('run', foreground='green')
        self.output_console.tag_config('error', foreground='red')
        self.output_console.tag_config('normal', foreground='black')
        self.output_console.tag_config('warning', foreground='orange')
        self.output_console.tag_config('stdout', foreground='gray')

    def reset_output(self):
        """ Clear all the output console """
        #self.output_console.config(state=NORMAL)
        self.output_console.delete(1.0, END)
        self.begin()

        self.write("MrPython v.{} -- mode {}\n".format(
            version.version_string(), tr(self.mode)))
        #self.output_console.config(state=DISABLED)

    def change_mode(self, mode):
        """ When the mode change : clear the output console and display
            the new mode """
        self.mode = mode
        self.reset_output()
        #self.switch_input_status(False)

    def write_report(self, status, report):
        tag = 'run'
        if not status:
            tag = 'error'

        self.write(report.header, tags=(tag))
        for error in report.convention_errors:
            self.write(str(error), tags=(error.severity))

        if not status:
            for error in report.compilation_errors:
                self.write(str(error), tags=(error.severity))
            for error in report.execution_errors:
                self.write(str(error), tags=(error.severity))
        else:
            for error in report.execution_errors:
                self.write(str(error), tags=(error.severity))

            self.write(str(report.output), tags=('stdout'))
            if report.result is not None:
                self.write(repr(report.result), tags=('normal'))

        self.write(report.footer, tags=(tag))

    def evaluate_action(self, *args):
        """ Evaluate the expression in the input console """
        expr = self.input_console.get()
        local_interpreter = False
        if self.interpreter is None:
            self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                                "<<console>>")
            local_interpreter = True
            self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        # the call back
        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            if ok:
                self.input_history.record(expr)

            self.input_console.delete(0, END)
            self.write_report(ok, report)

            if local_interpreter:
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.run_evaluation(expr, callback)

    def history_up_action(self, event=None):
        entry = self.input_history.move_past()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def history_down_action(self, event=None):
        entry = self.input_history.move_future()
        if entry is not None:
            self.input_console.delete(0, END)
            self.input_console.insert(0, entry)

    def switch_input_status(self, on):
        """ Enable or disable the evaluation bar and button """
        stat = None
        bg = None
        if on:
            stat = 'normal'
            bg = '#FFA500'
        else:
            stat = 'disabled'
            bg = '#775F57'
        self.input_console.config(state=stat, background=bg)
        self.eval_button.config(state=stat)

    def run(self, filename):
        """ Run the program in the current editor : execute, print results """
        # Reset the output first
        self.reset_output()
        # A new PyInterpreter is created each time code is run
        # It is then kept for other actions, like evaluation
        if self.interpreter is not None:
            self.interpreter.kill()
            self.app.running_interpreter_proxy = None

        self.interpreter = InterpreterProxy(self.app.root, self.app.mode,
                                            filename)
        self.app.running_interpreter_proxy = self.interpreter

        callback_called = False

        def callback(ok, report):
            nonlocal callback_called
            if callback_called:
                return
            else:
                callback_called = True

            #print("[console] CALLBACK: exec ok ? {}  report={}".format(ok, report))
            self.write_report(ok, report)

            # Enable or disable the evaluation bar according to the execution status
            if ok:
                self.input_console.focus_set()
                #self.switch_input_status(True)
            else:
                # kill the interpreter
                self.interpreter.kill()
                self.interpreter = None
                self.app.running_interpreter_proxy = None

            self.app.icon_widget.disable_icon_running()
            self.app.running_interpreter_callback = None

        # non-blocking call
        self.app.icon_widget.enable_icon_running()
        self.app.running_interpreter_callback = callback
        self.interpreter.execute(callback)

    def no_file_to_run_message(self):
        self.reset_output()
        self.write("=== No file to run ===", "error")

    def write(self, s, tags=()):
        """ Write into the output console """
        if isinstance(s, str) and len(s) and max(s) > '\uffff':
            # Tk doesn't support outputting non-BMP characters
            # Let's assume what printed string is not very long,
            # find first non-BMP character and construct informative
            # UnicodeEncodeError exception.
            for start, char in enumerate(s):
                if char > '\uffff':
                    break
            raise UnicodeEncodeError("UCS-2", char, start, start + 1,
                                     'Non-BMP character not supported in Tk')
        try:
            self.output_console.mark_gravity("iomark", "right")
            if isinstance(s, (bytes, bytes)):
                s = s.decode(IOBinding.encoding, "replace")
            #self.output_console.configure(state='normal')
            self.output_console.insert("iomark", s, tags)
            #self.output_console.configure(state='disabled')
            self.output_console.see("iomark")
            self.output_console.update()
            self.output_console.mark_gravity("iomark", "left")
        except:
            raise
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt

    def begin(self):
        """ Display some informations in the output console at the beginning """
        self.output_console.mark_set("iomark", "insert")
        sys.displayhook = rpc.displayhook
        self.write("Python %s on %s\n" % (sys.version, sys.platform))

    def readline(self):
        save = self.READING
        try:
            self.READING = 1
        finally:
            self.READING = save
        if self._stop_readline_flag:
            self._stop_readline_flag = False
            return ""
        line = self.output_console.get("iomark", "end-1c")
        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
            line = "\n"
        self.reset_output()
        if self.canceled:
            self.canceled = 0
            raise KeyboardInterrupt
        if self.endoffile:
            self.endoffile = 0
            line = ""
        return line

    def reset_undo(self):
        self.undo.reset_undo()