Skip to content

Commit 07c44d1

Browse files
Backport PR #521 on branch 3.x (PR: Add logic to handle traceback color configuration) (#523)
1 parent b1871e8 commit 07c44d1

File tree

6 files changed

+192
-15
lines changed

6 files changed

+192
-15
lines changed

spyder_kernels/console/kernel.py

+28-7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from spyder_kernels.utils.mpl import automatic_backend, MPL_BACKENDS_TO_SPYDER
4848
from spyder_kernels.utils.nsview import (
4949
get_remote_data, make_remote_view, get_size)
50+
from spyder_kernels.utils.style import create_style_class
5051
from spyder_kernels.console.shell import SpyderShell
5152
from spyder_kernels.comms.utils import WriteContext
5253

@@ -639,6 +640,8 @@ def set_configuration(self, conf):
639640
ret["special_kernel_error"] = value
640641
elif key == "color scheme":
641642
self.set_color_scheme(value)
643+
elif key == "traceback_highlight_style":
644+
self.set_traceback_syntax_highlighting(value)
642645
elif key == "jedi_completer":
643646
self.set_jedi_completer(value)
644647
elif key == "greedy_completer":
@@ -657,13 +660,31 @@ def set_configuration(self, conf):
657660
return ret
658661

659662
def set_color_scheme(self, color_scheme):
660-
if color_scheme == "dark":
661-
# Needed to change the colors of tracebacks
662-
self.shell.run_line_magic("colors", "linux")
663-
self.set_sympy_forecolor(background_color='dark')
664-
elif color_scheme == "light":
665-
self.shell.run_line_magic("colors", "lightbg")
666-
self.set_sympy_forecolor(background_color='light')
663+
self.shell.set_spyder_theme(color_scheme)
664+
self.set_sympy_forecolor(background_color=color_scheme)
665+
self.set_traceback_highlighting(color_scheme)
666+
667+
def set_traceback_highlighting(self, color_scheme):
668+
"""Set the traceback highlighting color."""
669+
color = 'bg:ansired' if color_scheme == 'dark' else 'bg:ansiyellow'
670+
from IPython.core.ultratb import VerboseTB
671+
672+
if getattr(VerboseTB, 'tb_highlight', None) is not None:
673+
VerboseTB.tb_highlight = color
674+
elif getattr(VerboseTB, '_tb_highlight', None) is not None:
675+
VerboseTB._tb_highlight = color
676+
677+
def set_traceback_syntax_highlighting(self, syntax_style):
678+
"""Set the traceback syntax highlighting style."""
679+
import IPython.core.ultratb
680+
from IPython.core.ultratb import VerboseTB
681+
682+
IPython.core.ultratb.get_style_by_name = create_style_class
683+
684+
if getattr(VerboseTB, 'tb_highlight_style', None) is not None:
685+
VerboseTB.tb_highlight_style = syntax_style
686+
elif getattr(VerboseTB, '_tb_highlight_style', None) is not None:
687+
VerboseTB._tb_highlight_style = syntax_style
667688

668689
def get_cwd(self):
669690
"""Get current working directory."""

spyder_kernels/console/shell.py

+14
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__(self, *args, **kwargs):
5656
self._allow_kbdint = False
5757
self.register_debugger_sigint()
5858
self.update_gui_frontend = False
59+
self._spyder_theme = 'dark'
5960

6061
# register post_execute
6162
self.events.register('post_execute', self.do_post_execute)
@@ -84,6 +85,19 @@ def _showtraceback(self, etype, evalue, stb):
8485
stb = ['']
8586
super(SpyderShell, self)._showtraceback(etype, evalue, stb)
8687

88+
def set_spyder_theme(self, theme):
89+
"""Set the theme for the console."""
90+
self._spyder_theme = theme
91+
if theme == "dark":
92+
# Needed to change the colors of tracebacks
93+
self.run_line_magic("colors", "linux")
94+
elif theme == "light":
95+
self.run_line_magic("colors", "lightbg")
96+
97+
def get_spyder_theme(self):
98+
"""Get the theme for the console."""
99+
return self._spyder_theme
100+
87101
def enable_matplotlib(self, gui=None):
88102
"""Enable matplotlib."""
89103
if gui is None or gui.lower() == "auto":

spyder_kernels/console/start.py

-3
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ def kernel_config():
6969
# Don't load nor save history in our IPython consoles.
7070
spy_cfg.HistoryAccessor.enabled = False
7171

72-
# Until we implement Issue 1052
73-
spy_cfg.InteractiveShell.xmode = 'Plain'
74-
7572
# Jedi completer.
7673
jedi_o = os.environ.get('SPY_JEDI_O') == 'True'
7774
spy_cfg.IPCompleter.use_jedi = jedi_o

spyder_kernels/customize/code_runner.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,14 @@ class SpyderCodeRunner(Magics):
143143
Functions and magics related to code execution, debugging, profiling, etc.
144144
"""
145145
def __init__(self, *args, **kwargs):
146+
super().__init__(*args, **kwargs)
147+
146148
self.show_global_msg = True
147149
self.show_invalid_syntax_msg = True
148150
self.umr = UserModuleReloader(
149-
namelist=os.environ.get("SPY_UMR_NAMELIST", None)
151+
namelist=os.environ.get("SPY_UMR_NAMELIST", None),
152+
shell=self.shell,
150153
)
151-
super().__init__(*args, **kwargs)
152154

153155
@runfile_arguments
154156
@needs_local_scope

spyder_kernels/customize/umr.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class UserModuleReloader:
2020
namelist [list]: blacklist in terms of module name
2121
"""
2222

23-
def __init__(self, namelist=None, pathlist=None):
23+
def __init__(self, namelist=None, pathlist=None, shell=None):
2424
if namelist is None:
2525
namelist = []
2626
else:
@@ -45,6 +45,7 @@ def __init__(self, namelist=None, pathlist=None):
4545
self.namelist = namelist + spy_modules + mpl_modules + other_modules
4646

4747
self.pathlist = pathlist
48+
self._shell = shell
4849

4950
# List of previously loaded modules
5051
self.previous_modules = list(sys.modules.keys())
@@ -92,7 +93,11 @@ def run(self):
9293
# Report reloaded modules
9394
if self.verbose and modnames_to_reload:
9495
modnames = modnames_to_reload
95-
print("\x1b[4;33m%s\x1b[24m%s\x1b[0m"
96-
% ("Reloaded modules", ": "+", ".join(modnames)))
96+
colors = {"dark": "33", "light": "31"}
97+
color = colors["dark"]
98+
if self._shell:
99+
color = colors[self._shell.get_spyder_theme()]
100+
content = ": "+", ".join(modnames)
101+
print(f"\x1b[4;{color}mReloaded modules\x1b[24m{content}\x1b[0m")
97102

98103
return modnames_to_reload

spyder_kernels/utils/style.py

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# -*- coding: utf-8 -*-
2+
# -----------------------------------------------------------------------------
3+
# Copyright (c) 2009- Spyder Kernels Contributors
4+
#
5+
# Licensed under the terms of the MIT License
6+
# (see spyder_kernels/__init__.py for details)
7+
# -----------------------------------------------------------------------------
8+
9+
"""
10+
Style for IPython Console
11+
"""
12+
13+
# Third party imports
14+
from pygments.style import Style
15+
from pygments.token import (
16+
Name,
17+
Keyword,
18+
Comment,
19+
String,
20+
Number,
21+
Punctuation,
22+
Operator,
23+
)
24+
25+
26+
def create_pygments_dict(color_scheme_dict):
27+
"""
28+
Create a dictionary that saves the given color scheme as a
29+
Pygments style.
30+
"""
31+
32+
def give_font_weight(is_bold):
33+
if is_bold:
34+
return "bold"
35+
else:
36+
return ""
37+
38+
def give_font_style(is_italic):
39+
if is_italic:
40+
return "italic"
41+
else:
42+
return ""
43+
44+
color_scheme = color_scheme_dict
45+
46+
fon_c, fon_fw, fon_fs = color_scheme["normal"]
47+
font_color = fon_c
48+
font_font_weight = give_font_weight(fon_fw)
49+
font_font_style = give_font_style(fon_fs)
50+
51+
key_c, key_fw, key_fs = color_scheme["keyword"]
52+
keyword_color = key_c
53+
keyword_font_weight = give_font_weight(key_fw)
54+
keyword_font_style = give_font_style(key_fs)
55+
56+
bui_c, bui_fw, bui_fs = color_scheme["builtin"]
57+
builtin_color = bui_c
58+
builtin_font_weight = give_font_weight(bui_fw)
59+
builtin_font_style = give_font_style(bui_fs)
60+
61+
str_c, str_fw, str_fs = color_scheme["string"]
62+
string_color = str_c
63+
string_font_weight = give_font_weight(str_fw)
64+
string_font_style = give_font_style(str_fs)
65+
66+
num_c, num_fw, num_fs = color_scheme["number"]
67+
number_color = num_c
68+
number_font_weight = give_font_weight(num_fw)
69+
number_font_style = give_font_style(num_fs)
70+
71+
com_c, com_fw, com_fs = color_scheme["comment"]
72+
comment_color = com_c
73+
comment_font_weight = give_font_weight(com_fw)
74+
comment_font_style = give_font_style(com_fs)
75+
76+
def_c, def_fw, def_fs = color_scheme["definition"]
77+
definition_color = def_c
78+
definition_font_weight = give_font_weight(def_fw)
79+
definition_font_style = give_font_style(def_fs)
80+
81+
ins_c, ins_fw, ins_fs = color_scheme["instance"]
82+
instance_color = ins_c
83+
instance_font_weight = give_font_weight(ins_fw)
84+
instance_font_style = give_font_style(ins_fs)
85+
86+
font_token = font_font_style + " " + font_font_weight + " " + font_color
87+
definition_token = (
88+
definition_font_style
89+
+ " "
90+
+ definition_font_weight
91+
+ " "
92+
+ definition_color
93+
)
94+
builtin_token = (
95+
builtin_font_style + " " + builtin_font_weight + " " + builtin_color
96+
)
97+
instance_token = (
98+
instance_font_style + " " + instance_font_weight + " " + instance_color
99+
)
100+
keyword_token = (
101+
keyword_font_style + " " + keyword_font_weight + " " + keyword_color
102+
)
103+
comment_token = (
104+
comment_font_style + " " + comment_font_weight + " " + comment_color
105+
)
106+
string_token = (
107+
string_font_style + " " + string_font_weight + " " + string_color
108+
)
109+
number_token = (
110+
number_font_style + " " + number_font_weight + " " + number_color
111+
)
112+
113+
syntax_style_dic = {
114+
Name: font_token.strip(),
115+
Name.Class: definition_token.strip(),
116+
Name.Function: definition_token.strip(),
117+
Name.Builtin: builtin_token.strip(),
118+
Name.Builtin.Pseudo: instance_token.strip(),
119+
Keyword: keyword_token.strip(),
120+
Keyword.Type: builtin_token.strip(),
121+
Comment: comment_token.strip(),
122+
String: string_token.strip(),
123+
Number: number_token.strip(),
124+
Punctuation: font_token.strip(),
125+
Operator.Word: keyword_token.strip(),
126+
}
127+
128+
return syntax_style_dic
129+
130+
131+
def create_style_class(color_scheme_dict):
132+
"""Create a Pygments Style class with the given color scheme."""
133+
134+
class StyleClass(Style):
135+
default_style = ""
136+
styles = create_pygments_dict(color_scheme_dict)
137+
138+
return StyleClass

0 commit comments

Comments
 (0)