Customizing the IPython interface
Syntax highlighting in IPython is a simple feature, but it’s a great one. The green-red back-and-forth between input and response makes scrolling back through a terminal session smooth and readable, and coloring individual tokens by semantic meaning is both informative and easy on the eyes, obviously (that’s why we do it in IDEs).
However, if you use any kind of a custom terminal theme, this experience gets bumpier. IPython will generally lean on the basic ANSI colors set by your terminal, but (at least in my case – running zshell within Alacritty/Tmux) also summons a few colors that are definitely not in the specified color set, and can be pretty hard to read (bad) and ugly (obviously way worse).
Some of these are coming from my terminal – the light-green in the prompt, and the red traceback boundaries are correct. However, the dark green/bold, and especially the bolded blue in the traceback, are bypassing my terminal config, defined directly from IPython. And the blue, in particular, is basically unreadable against my shell background.
There are a few out-of-the-box changes that can help, sort of. The IPython config provides a few options:
## Set the color scheme (NoColor, Neutral, Linux, or LightBG).
# c.InteractiveShell.colors = 'Neutral'
# ...
## The name or class of a Pygments style to use for syntax highlighting. To see
# available styles, run `pygmentize -L styles`.
# c.TerminalInteractiveShell.highlighting_style = 'gruvbox-dark'
## Override highlighting format for specific tokens
# c.TerminalInteractiveShell.highlighting_style_overrides = {}
InteractiveShell.colors
sets a general color scheme, affecting input syntax highlighting and feedback, but only offers four theme options. TerminalInteractiveShell.highlighting_style
(and TerminalInteractiveShell.highlighting_style_overrides
) provide many more choices by allowing the user to specify the colors that IPython provides to Pygments for highlightingtext, but can’t alter the colors used in feedback text (eg exception tracebacks).
Now, the Pygments stuff is pretty great. It’s a regex-based highlighter, which means anyone used to tools like Treesitter will be a little disappointed, but it still affords a decent amount of granularity for color choices, and lets you define general token classes (like Number) and then provide different selections for subcomponents (like Number.Float). I couldn’t find a master list of token types used in the Python lexer, though, so I had to reconstruct an approximation from the source code:
Token | What’s included |
---|---|
Text | Catch-all for anything not otherwise defined |
String | Includes multiline comments, too |
String.Escape | Escape sequences within strings |
String.Interpol | The brackets in f-strings |
Number | All numbers |
Number.Integer | Ints |
Number.Float | Floats |
Number.Bin | Binary numbers |
Number.Oct | Octals |
Number.Hex | Hex values |
Comment | Single-line comments |
Keyword | Lots of members – stuff like def , class , True and False , if /elif /else etc. |
Keyword.Constant | True , False , and None |
Keyword.Reserved | A few core tokens like except , finally , if , raise , while , etc |
Keyword.Namespace | import , and from /import |
Operator | Everything you’d expect, and also the Walrus |
Operator.Word | in , is , and , or , not |
Punctuation | Brackets and parens |
Name.Builtin | Builtin functions like all() , max() , iter() |
Name.Builtin.Pseudo | Stuff like self , Ellipsis , NotImplemented , cls |
Name.Exception | All of the builtin Exception types |
Name.Function | the name of the function in the definition |
Name.Function.Magic | Builtin dunder methods |
Name.Variable.Magic | Builtin dunder object attributes |
Name.Decorator | Both the @ and the decorator name |
Name.Class | I think this should be the name in the class definition but couldn’t confirm experimentally |
This enables some progress:
But tracebacks are still a catastrophe:
You can use the InteractiveShell
setting mentioned above to turn off these colors entirely (nocolor
) or make them insane (LightBG
). But, even though Pygmentize does provide a lexer for tracebacks, you can’t use any kind of custom colors there. So, you’re stuck.
On another note, you can also configure the prompts, which is cool. The IPython config docs are a bit better here than on the color scheme, although I think the actual prompt file is more informative than the example they point to.
from IPython.terminal.prompts import Prompts, Token
class MyPrompt(Prompts):
def in_prompt_tokens(self, cli=None):
return [(Token.Prompt, '\e[0m \033[38;2;92;28;57m \uE0B0')]
## Class used to generate Prompt token for prompt_toolkit
c.TerminalInteractiveShell.prompts_class = MyPrompt
Powerline glyphs like the above will work, but colorization using ANSI escape codes will… unfortunately not, which is unsurprising.
You are at the whims of the Pygments tokens that you provide, and it’s not clear to me whether there’s any hope of overriding the token colors outside of the parent InteractiveShell.colors
specification. Anyway, here’s to hoping that someone jumps on this stuff in a future IPython PR.