Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add logic to handle traceback color configuration #521

Merged
merged 8 commits into from
Nov 26, 2024
35 changes: 28 additions & 7 deletions spyder_kernels/console/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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":
Expand All @@ -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."""
Expand Down
14 changes: 14 additions & 0 deletions spyder_kernels/console/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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":
Expand Down
3 changes: 0 additions & 3 deletions spyder_kernels/console/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions spyder_kernels/customize/code_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ class SpyderCodeRunner(Magics):
def __init__(self, *args, **kwargs):
self.show_global_msg = True
self.show_invalid_syntax_msg = True
super().__init__(*args, **kwargs)
dalthviz marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
11 changes: 8 additions & 3 deletions spyder_kernels/customize/umr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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())
Expand Down Expand Up @@ -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()]
print("\x1b[4;%sm%s\x1b[24m%s\x1b[0m"
% (color, "Reloaded modules", ": "+", ".join(modnames)))
ccordoba12 marked this conversation as resolved.
Show resolved Hide resolved

return modnames_to_reload
138 changes: 138 additions & 0 deletions spyder_kernels/utils/style.py
ccordoba12 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -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
Loading