From fb0686376ad933806f5847c71b1529cca349bcbc Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 26 Nov 2024 22:58:18 -0500 Subject: [PATCH 1/2] Backport PR #22965 on branch 6.x (PR: Initial traceback setup to use selected syntax style (IPython Console)) --- spyder/app/tests/test_mainwindow.py | 2 +- spyder/plugins/ipythonconsole/widgets/shell.py | 12 +++++++++++- spyder/utils/misc.py | 15 ++++++++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 107940e3eb9..ea66fb499be 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6499,7 +6499,7 @@ def test_clickable_ipython_tracebacks(main_window, qtbot, tmp_path): control.setFocus() find_widget = main_window.ipyconsole.get_widget().find_widget find_widget.show() - find_widget.search_text.lineEdit().setText(' File') + find_widget.search_text.lineEdit().setText('File') find_widget.find_previous() # Position mouse on top of that line diff --git a/spyder/plugins/ipythonconsole/widgets/shell.py b/spyder/plugins/ipythonconsole/widgets/shell.py index 79294458f06..4a25586c386 100644 --- a/spyder/plugins/ipythonconsole/widgets/shell.py +++ b/spyder/plugins/ipythonconsole/widgets/shell.py @@ -188,6 +188,10 @@ def __init__( self._init_kernel_setup = False self._is_banner_shown = False + # Set bright colors instead of bold formatting for better traceback + # readability. + self._ansi_processor.bold_text_enabled = False + if handlers is None: handlers = {} else: @@ -697,7 +701,7 @@ def set_color_scheme(self, color_scheme, reset=True): self.style_sheet, dark_color = create_qss_style(color_scheme) self.syntax_style = color_scheme self._style_sheet_changed() - self._syntax_style_changed() + self._syntax_style_changed(changed={}) if reset: self.reset(clear=True) if not self.spyder_kernel_ready: @@ -1438,6 +1442,12 @@ def _syntax_style_changed(self, changed=None): if self.syntax_style: self._highlighter._style = create_style_class(self.syntax_style) self._highlighter._clear_caches() + if changed is None: + return + self.set_kernel_configuration( + "traceback_highlight_style", + get_color_scheme(self.syntax_style), + ) else: self._highlighter.set_style_sheet(self.style_sheet) diff --git a/spyder/utils/misc.py b/spyder/utils/misc.py index 7d8ee7126c9..50d5b0bbbd8 100644 --- a/spyder/utils/misc.py +++ b/spyder/utils/misc.py @@ -154,16 +154,21 @@ def remove_backslashes(path): def get_error_match(text): """Check if text contains a Python error.""" - # For regular Python tracebacks and IPython 7 or less. + # For regular Python tracebacks and IPython 7 or less (xmode plain). match_python = re.match(r' File "(.*)", line (\d*)', text) if match_python is not None: return match_python - # For IPython 8+ tracebacks. + # For IPython 8+ tracebacks (xmode plain). # Fixes spyder-ide/spyder#20407 - ipython8_match = re.match(r' File (.*):(\d*)', text) - if ipython8_match is not None: - return ipython8_match + ipython8_plain_match = re.match(r' File (.*):(\d*)', text) + if ipython8_plain_match is not None: + return ipython8_plain_match + + # For IPython 8+ tracebacks (xmode context). + ipython8_context_match = re.match(r'File (.*):(\d*)', text) + if ipython8_context_match is not None: + return ipython8_context_match return False From c63ab72419b37ffc23dc62da00aa9dbdc4b54cae Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 26 Nov 2024 23:16:54 -0500 Subject: [PATCH 2/2] git subrepo pull external-deps/spyder-kernels subrepo: subdir: "external-deps/spyder-kernels" merged: "07c44d143c" upstream: origin: "https://github.com/spyder-ide/spyder-kernels.git" branch: "3.x" commit: "07c44d143c" git-subrepo: version: "0.4.9" origin: "https://github.com/ingydotnet/git-subrepo" commit: "ea10886" --- external-deps/spyder-kernels/.gitrepo | 6 +- .../spyder_kernels/console/kernel.py | 35 ++++- .../spyder_kernels/console/shell.py | 14 ++ .../spyder_kernels/console/start.py | 3 - .../spyder_kernels/customize/code_runner.py | 6 +- .../spyder_kernels/customize/umr.py | 11 +- .../spyder_kernels/utils/style.py | 138 ++++++++++++++++++ 7 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 external-deps/spyder-kernels/spyder_kernels/utils/style.py diff --git a/external-deps/spyder-kernels/.gitrepo b/external-deps/spyder-kernels/.gitrepo index d2ecf8748bc..e4377d80c32 100644 --- a/external-deps/spyder-kernels/.gitrepo +++ b/external-deps/spyder-kernels/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/spyder-ide/spyder-kernels.git branch = 3.x - commit = b1871e8f6fa9b5e4d34b52c513be1fccc8d22e14 - parent = 9625c62ceaaf1b9007194b3b01b7965d553248c7 + commit = 07c44d143ce85ea19b2be6cbbe1863f117f5d90b + parent = fb0686376ad933806f5847c71b1529cca349bcbc method = merge - cmdver = 0.4.3 + cmdver = 0.4.9 diff --git a/external-deps/spyder-kernels/spyder_kernels/console/kernel.py b/external-deps/spyder-kernels/spyder_kernels/console/kernel.py index ad16c08475a..391c7392b24 100644 --- a/external-deps/spyder-kernels/spyder_kernels/console/kernel.py +++ b/external-deps/spyder-kernels/spyder_kernels/console/kernel.py @@ -47,6 +47,7 @@ from spyder_kernels.utils.mpl import automatic_backend, MPL_BACKENDS_TO_SPYDER from spyder_kernels.utils.nsview import ( get_remote_data, make_remote_view, get_size) +from spyder_kernels.utils.style import create_style_class from spyder_kernels.console.shell import SpyderShell from spyder_kernels.comms.utils import WriteContext @@ -639,6 +640,8 @@ def set_configuration(self, conf): ret["special_kernel_error"] = value elif key == "color scheme": self.set_color_scheme(value) + elif key == "traceback_highlight_style": + self.set_traceback_syntax_highlighting(value) elif key == "jedi_completer": self.set_jedi_completer(value) elif key == "greedy_completer": @@ -657,13 +660,31 @@ def set_configuration(self, conf): return ret def set_color_scheme(self, color_scheme): - if color_scheme == "dark": - # Needed to change the colors of tracebacks - self.shell.run_line_magic("colors", "linux") - self.set_sympy_forecolor(background_color='dark') - elif color_scheme == "light": - self.shell.run_line_magic("colors", "lightbg") - self.set_sympy_forecolor(background_color='light') + self.shell.set_spyder_theme(color_scheme) + self.set_sympy_forecolor(background_color=color_scheme) + self.set_traceback_highlighting(color_scheme) + + def set_traceback_highlighting(self, color_scheme): + """Set the traceback highlighting color.""" + color = 'bg:ansired' if color_scheme == 'dark' else 'bg:ansiyellow' + from IPython.core.ultratb import VerboseTB + + if getattr(VerboseTB, 'tb_highlight', None) is not None: + VerboseTB.tb_highlight = color + elif getattr(VerboseTB, '_tb_highlight', None) is not None: + VerboseTB._tb_highlight = color + + def set_traceback_syntax_highlighting(self, syntax_style): + """Set the traceback syntax highlighting style.""" + import IPython.core.ultratb + from IPython.core.ultratb import VerboseTB + + IPython.core.ultratb.get_style_by_name = create_style_class + + if getattr(VerboseTB, 'tb_highlight_style', None) is not None: + VerboseTB.tb_highlight_style = syntax_style + elif getattr(VerboseTB, '_tb_highlight_style', None) is not None: + VerboseTB._tb_highlight_style = syntax_style def get_cwd(self): """Get current working directory.""" diff --git a/external-deps/spyder-kernels/spyder_kernels/console/shell.py b/external-deps/spyder-kernels/spyder_kernels/console/shell.py index 3e9224c9df6..0de22d4345b 100644 --- a/external-deps/spyder-kernels/spyder_kernels/console/shell.py +++ b/external-deps/spyder-kernels/spyder_kernels/console/shell.py @@ -56,6 +56,7 @@ def __init__(self, *args, **kwargs): self._allow_kbdint = False self.register_debugger_sigint() self.update_gui_frontend = False + self._spyder_theme = 'dark' # register post_execute self.events.register('post_execute', self.do_post_execute) @@ -84,6 +85,19 @@ def _showtraceback(self, etype, evalue, stb): stb = [''] super(SpyderShell, self)._showtraceback(etype, evalue, stb) + def set_spyder_theme(self, theme): + """Set the theme for the console.""" + self._spyder_theme = theme + if theme == "dark": + # Needed to change the colors of tracebacks + self.run_line_magic("colors", "linux") + elif theme == "light": + self.run_line_magic("colors", "lightbg") + + def get_spyder_theme(self): + """Get the theme for the console.""" + return self._spyder_theme + def enable_matplotlib(self, gui=None): """Enable matplotlib.""" if gui is None or gui.lower() == "auto": diff --git a/external-deps/spyder-kernels/spyder_kernels/console/start.py b/external-deps/spyder-kernels/spyder_kernels/console/start.py index 600b656c02e..b8a423af2a9 100644 --- a/external-deps/spyder-kernels/spyder_kernels/console/start.py +++ b/external-deps/spyder-kernels/spyder_kernels/console/start.py @@ -69,9 +69,6 @@ def kernel_config(): # Don't load nor save history in our IPython consoles. spy_cfg.HistoryAccessor.enabled = False - # Until we implement Issue 1052 - spy_cfg.InteractiveShell.xmode = 'Plain' - # Jedi completer. jedi_o = os.environ.get('SPY_JEDI_O') == 'True' spy_cfg.IPCompleter.use_jedi = jedi_o diff --git a/external-deps/spyder-kernels/spyder_kernels/customize/code_runner.py b/external-deps/spyder-kernels/spyder_kernels/customize/code_runner.py index 9917abb9999..8f3ac12cf41 100644 --- a/external-deps/spyder-kernels/spyder_kernels/customize/code_runner.py +++ b/external-deps/spyder-kernels/spyder_kernels/customize/code_runner.py @@ -143,12 +143,14 @@ class SpyderCodeRunner(Magics): Functions and magics related to code execution, debugging, profiling, etc. """ def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.show_global_msg = True self.show_invalid_syntax_msg = True self.umr = UserModuleReloader( - namelist=os.environ.get("SPY_UMR_NAMELIST", None) + namelist=os.environ.get("SPY_UMR_NAMELIST", None), + shell=self.shell, ) - super().__init__(*args, **kwargs) @runfile_arguments @needs_local_scope diff --git a/external-deps/spyder-kernels/spyder_kernels/customize/umr.py b/external-deps/spyder-kernels/spyder_kernels/customize/umr.py index e779ec336bd..c110515823d 100644 --- a/external-deps/spyder-kernels/spyder_kernels/customize/umr.py +++ b/external-deps/spyder-kernels/spyder_kernels/customize/umr.py @@ -20,7 +20,7 @@ class UserModuleReloader: namelist [list]: blacklist in terms of module name """ - def __init__(self, namelist=None, pathlist=None): + def __init__(self, namelist=None, pathlist=None, shell=None): if namelist is None: namelist = [] else: @@ -45,6 +45,7 @@ def __init__(self, namelist=None, pathlist=None): self.namelist = namelist + spy_modules + mpl_modules + other_modules self.pathlist = pathlist + self._shell = shell # List of previously loaded modules self.previous_modules = list(sys.modules.keys()) @@ -92,7 +93,11 @@ def run(self): # Report reloaded modules if self.verbose and modnames_to_reload: modnames = modnames_to_reload - print("\x1b[4;33m%s\x1b[24m%s\x1b[0m" - % ("Reloaded modules", ": "+", ".join(modnames))) + colors = {"dark": "33", "light": "31"} + color = colors["dark"] + if self._shell: + color = colors[self._shell.get_spyder_theme()] + content = ": "+", ".join(modnames) + print(f"\x1b[4;{color}mReloaded modules\x1b[24m{content}\x1b[0m") return modnames_to_reload diff --git a/external-deps/spyder-kernels/spyder_kernels/utils/style.py b/external-deps/spyder-kernels/spyder_kernels/utils/style.py new file mode 100644 index 00000000000..255ce9593da --- /dev/null +++ b/external-deps/spyder-kernels/spyder_kernels/utils/style.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright (c) 2009- Spyder Kernels Contributors +# +# Licensed under the terms of the MIT License +# (see spyder_kernels/__init__.py for details) +# ----------------------------------------------------------------------------- + +""" +Style for IPython Console +""" + +# Third party imports +from pygments.style import Style +from pygments.token import ( + Name, + Keyword, + Comment, + String, + Number, + Punctuation, + Operator, +) + + +def create_pygments_dict(color_scheme_dict): + """ + Create a dictionary that saves the given color scheme as a + Pygments style. + """ + + def give_font_weight(is_bold): + if is_bold: + return "bold" + else: + return "" + + def give_font_style(is_italic): + if is_italic: + return "italic" + else: + return "" + + color_scheme = color_scheme_dict + + fon_c, fon_fw, fon_fs = color_scheme["normal"] + font_color = fon_c + font_font_weight = give_font_weight(fon_fw) + font_font_style = give_font_style(fon_fs) + + key_c, key_fw, key_fs = color_scheme["keyword"] + keyword_color = key_c + keyword_font_weight = give_font_weight(key_fw) + keyword_font_style = give_font_style(key_fs) + + bui_c, bui_fw, bui_fs = color_scheme["builtin"] + builtin_color = bui_c + builtin_font_weight = give_font_weight(bui_fw) + builtin_font_style = give_font_style(bui_fs) + + str_c, str_fw, str_fs = color_scheme["string"] + string_color = str_c + string_font_weight = give_font_weight(str_fw) + string_font_style = give_font_style(str_fs) + + num_c, num_fw, num_fs = color_scheme["number"] + number_color = num_c + number_font_weight = give_font_weight(num_fw) + number_font_style = give_font_style(num_fs) + + com_c, com_fw, com_fs = color_scheme["comment"] + comment_color = com_c + comment_font_weight = give_font_weight(com_fw) + comment_font_style = give_font_style(com_fs) + + def_c, def_fw, def_fs = color_scheme["definition"] + definition_color = def_c + definition_font_weight = give_font_weight(def_fw) + definition_font_style = give_font_style(def_fs) + + ins_c, ins_fw, ins_fs = color_scheme["instance"] + instance_color = ins_c + instance_font_weight = give_font_weight(ins_fw) + instance_font_style = give_font_style(ins_fs) + + font_token = font_font_style + " " + font_font_weight + " " + font_color + definition_token = ( + definition_font_style + + " " + + definition_font_weight + + " " + + definition_color + ) + builtin_token = ( + builtin_font_style + " " + builtin_font_weight + " " + builtin_color + ) + instance_token = ( + instance_font_style + " " + instance_font_weight + " " + instance_color + ) + keyword_token = ( + keyword_font_style + " " + keyword_font_weight + " " + keyword_color + ) + comment_token = ( + comment_font_style + " " + comment_font_weight + " " + comment_color + ) + string_token = ( + string_font_style + " " + string_font_weight + " " + string_color + ) + number_token = ( + number_font_style + " " + number_font_weight + " " + number_color + ) + + syntax_style_dic = { + Name: font_token.strip(), + Name.Class: definition_token.strip(), + Name.Function: definition_token.strip(), + Name.Builtin: builtin_token.strip(), + Name.Builtin.Pseudo: instance_token.strip(), + Keyword: keyword_token.strip(), + Keyword.Type: builtin_token.strip(), + Comment: comment_token.strip(), + String: string_token.strip(), + Number: number_token.strip(), + Punctuation: font_token.strip(), + Operator.Word: keyword_token.strip(), + } + + return syntax_style_dic + + +def create_style_class(color_scheme_dict): + """Create a Pygments Style class with the given color scheme.""" + + class StyleClass(Style): + default_style = "" + styles = create_pygments_dict(color_scheme_dict) + + return StyleClass