Pimp Your Python and Django Shells

Easy steps to beautify your Python/Django shells using IPython

Objective

  • To have an slick IPython REPL like this:

Background

IPython (Interactive Python) is 20 years old now. It is mature, has lots of features, and really popular. It’s an interactive shell, an eval() machine, and serves as the backend for various clients, like the IPython REPL shell (terminal client) and IPython/Jupyter Notebook (browser client).

The default Python REPL (Read-Eval-Print-Loop) shell has drawbacks, so IPython aimed to improve the UI/UX. Some of the notable IPython features:

  • Syntax highlighting (easier to read codes or spot mistakes)
  • Tab completion (less typing)
  • Auto-indentation (useful for code blocks like in for loop or def)
  • Introspection (e.g. foo? for docs, foo?? for source code)
  • Auto-reloading of modules (convenient/efficient)
  • Access to Unix shell commands (e.g. ls, pwd, cd)
  • Integration with Django shell
  • Sessions/history are saved

The first 3 alone are enough reasons to ditch the default Python REPL.

Jupyter (Julia-Python-R) Notebooks are popular on statisticians, scientists, researchers, etc. While there are lots of resources for customizing Jupyter themes, there’s only a handful for IPython REPL. And they are quite sparse, so need to dig the IPython docs, and even the source codes to customize the colors/prompts.

IPython Shell

Installation

It’s easy to install:

pip install ipython

Bundled Color Schemes

IPython bundles some color schemes: Neutral, Linux, LightBG, and NoColor. The default color scheme is called Neutral and could be invoked with the —-colors option:

ipython --colors=Neutral

Another color scheme is called Linux. Similarly, this could be invoked with:

ipython --colors=Linux

But these color schemes don’t look good, especially for dark terminals. Color scheme is a personal thing. So, users will most likely want to use their favorite color scheme too. Luckily, there’s a way.

Pygments

Pygments is a syntax highlighter written in Python. It’s a dependency for popular projects like Rich, Pdb++, PuDB, IPython, etc. Hence, when you install IPython, it’s also auto-installed.

Installed Themes

Pygment bundles a lot of themes. We could easily check the available/installed themes using:

pygmentize -L styles

These color schemes have screenshots in the Pygments website, so you’ll have idea on how they will look on the terminal.

IPython Theme

Zenburn Color Scheme

We could specify the color scheme in IPython when invoking the command. For example, to use the zenburn color scheme:

ipython --TerminalInteractiveShell.highlighting_style=zenburn

Python/IPython Override

We could then set these handy shell aliases in .bashrc or .zshrc so that invoking python or ipython will auto-invoke ipython with a theme:

alias python="ipython"  
alias ipython="ipython --TerminalInteractiveShell.highlighting_style=zenburn"

Font Styles

In the styles gallery, you will notice that some themes have italic/bold characters, like in the material and dracula themes. These depend mainly on your terminal. For example, the ligatures/styled letters are not supported in my fast Alacritty terminal, but it’s rendering fine on my iTerm2. Below is the screenshot when using iTerm2 with material theme notice that the import and _ are italicized:

One Dark Color Scheme

One Dark is my favorite theme. It was popularized by Atom and currently one of the most popular themes in VS Code, PyCharm, or Vim. All my editors, IDEs, and terminals are using it.

Fortunately, it’s available in Pygments also since 2 weeks ago. But, this means that it is not available yet on PyPI. The last PyPI update of pygments was almost 4 months ago (August 15, 2021). So, when you run the pygmentize -L styles, it’s not included in the list, and potentially the other recent themes as well.

Hence, I downloaded the latest version of pygments on GitHub, then copied the styles folder, and overridden the styles folder in my installed 2.10.0 version of pygments:

.../site-packages/pygments/styles

I could now run it with:

ipython --TerminalInteractiveShell.highlighting_style=one-dark

IPython Config

Other users might stop at this point since we already have a color scheme. But others might still be annoyed with the verbose IPython prompt, and its numbering and/or colors.

IPython Profile

To customize the various aspects of IPython, including the prompts, we could create a profile, so that we’ll put our customizations/overrides there:

ipython profile create

This will create the default profile called profile_default:

~/.ipython/profile_default

IPython will then auto-create a config file in it:

~/.ipython/profile_default/ipython_config.py

If you check the contents of config file, it contains the default settings of IPython, which are commented lines, and all of them are namespaced/prefixed with the global config (c) object.

Prompt Color

To change the prompt colors, we could leverage this attribute:

# Override highlighting format for specific tokens  
# c.TerminalInteractiveShell.highlighting_style_overrides = {}

For instance, you could put this anywhere in the config file:

from IPython.terminal.prompts import Token

YELLOW = '#FFFF5C'  
VIOLET = '#BD93F9'

c.TerminalInteractiveShell.highlighting_style_overrides = {  
    Token.Prompt: YELLOW,  
    Token.PromptNum: YELLOW,  
    Token.OutPrompt: VIOLET,  
    Token.OutPromptNum: VIOLET,  
}

Token.Prompt, Token.PromptNum, Token.OutPrompt, and Token.OutPromptNum correspond to the IPython input/output prompts, that is, the In [1]: and Out[2]:. This looks better:

Prompt Characters

The prompt colors are acceptable for me now, but it’s still too verbose, and I just want to have a minimal/slick prompt. We could easily override the default prompt by inheriting from the Prompts class, and overriding the prompt methods. You could put any characters in the tokens list like putting >>> to conform with the usual Python prompt:

from IPython.terminal.prompts import Prompts, Token

class MyPrompt(Prompts):

    def in_prompt_tokens(self, cli=None):  
        tokens = [  
            (Token.Prompt, 'λ  ')  
        ]  
        return tokens

    def out_prompt_tokens(self):  
        tokens = [  
            (Token.OutPrompt, 'Φ  ')  
        ]  
        return tokens

c.TerminalInteractiveShell.prompts_class = MyPrompt

This will now render the final form that is good enough for me, and consistent with my other terminal customizations:

Theme Config

Since we now have a config file, we could just set the theme in it also:

c.TerminalInteractiveShell.highlighting_style = 'one-dark'

So that we’ll not need to set it during the ipython invocation, or and no need to set the shell alias for it as discussed above.

Django Shell

If IPython is installed, the Django shell or shell_plus will utilize it, which will load also your custom profile/config. This is a sample when using the Django shell:

Auto-Reload

IPython has auto-reload mechanisms which is quite handy when working with Django shell since you don’t need to re-run the manage.py shell again if you updated your Django codes. Just need to load/invoke the autoreload IPython extension:

# Load  
% load_ext autoreload

# Invoke  
% autoreload

In the sample run below, random_hex_string() returns a random hex string, then I modified its definition/return value, saved the changes, loaded/invoked the IPython autoreload, then invoked random_hex_string() again. I intentionally hardcoded the IPython Auto-Reload FTW return text which is an invalid hex string just to demonstrate that the function definition was indeed modified. Effectively, the updated codes are executed without restarting the Django shell:

Key Takeaways

  • IPython is a mature project, very popular, and has lots of features and improvements over the default Python REPL. The important features are the syntax highlighting, tab completion, and auto-indentation.
  • The default IPython color/prompt is not acceptable for some users. It’s easy to customize them though.
  • IPython uses Pygments for coloring. We could list the available themes:

    pygmentize -L styles

  • We could set the IPython theme when we run ipython:

    ipython --TerminalInteractiveShell.highlighting_style=one-dark

  • We could create a default profile:

    ipython profile create

  • We could put our customizations (colors, prompts, etc) in the profile/config file:

    ~/.ipython/profile_default/ipython_config.py

  • Django shell or shell_plus will auto-load the IPython and its config file if it’s available. It could also auto-reload the imported modules which avoids re-running the shell.

  • IPython could replace the default Python REPL in IDEs’ terminals, like in PyCharm/VS Code.
  • IPython has many powerful features. Check out this interesting “Wait, IPython can do that?!” presentation, which includes discussion of IPython alternatives like bpython and ptpython.

Thank you for reading. If you found some value, kindly follow me, or give a reaction/comment on the article, or buy me a coffee. This will mean a lot to me and encourage me to create more content.