Photo by Arnold Francisca on Unsplash
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 ordef
) - 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
orshell_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 theshell
.- 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
andptpython
.
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.