As a result of going back to school for a CS grad degree, I've been using Python a ton for the first time in like 5 years. Over the weekend I was hacking away on a very debugging-heavy assignment, and I found some useful tricks for improving my Python REPL for debugging work, so I thought I'd share.
Get you a .pythonrc
As I was doing my debugging in the REPL, I started to build up a few functions that were very useful for that purpose. But every time I exited the REPL and started it up again, or opened up another REPL in a separate terminal, I had to copy and paste all those methods in again. What I realized I wanted was a Python equivalent of a .bashrc
, a script that runs every time I start the REPL to define my functions and set up my environment.
As far as I can tell (though correct me in a comment if I'm wrong), Python doesn't look for any specific setup files by default. However, it does check for a special environment variable, called PYTHONSTARTUP
, and if that variable is set to a path, it runs the file at that path. Perfect!
I'm using Bash, so I added this line to my .bashrc
file:
export PYTHONSTARTUP=~/.pythonrc.py
If you're in Windows land, you can add "PYTHONSTARTUP" to your account's environment variables by hitting the Start button, searching for "environment", and selecting "Edit environment variables for your account", then adding a new variable.
Note: You don't necessarily have to put the file in your home directory, or name it
.pythonrc.py
; you're telling Python where to look, so do what you want! Just make sure the variable points to your file, wherever you put it.
Code it up!
My ~/.pythonrc.py
file is a work in progress, but here's the contents so far:
### enable tab completion
import rlcompleter, readline
readline.parse_and_bind("tab: complete")
### import custom python utilities
import sys
from os.path import expanduser
sys.path.append(expanduser('~'))
from custom_utils import *
I've got two things in there:
- I manually enable tab-completion, since it doesn't seem to work by default.
- I import a custom module of handy functions. I decided to define them separately from this config file so that I could use them elsewhere (back to that in a sec). To do this, I have to add my home directory (where I keep the module) to the path, then
import *
from my module.
Note: Again, this module can be named anything and go anywhere, just make sure you're pointing to it correctly!
My custom utils
Here's the contents of my custom_utils.py
file. Again, it's a work in progress and I'm just starting with it, but I've found these methods handy for exploring the libraries I'm using for my coursework, and for debugging sessions (that's next!).
from math import ceil
def chunk(size, _iter):
""" split an iterable into chunks of the given size """
_num_groups = range(int(ceil(len(_iter)/float(size))))
return (_iter[n*size:(n+1)*size] for n in _num_groups)
def P(obj, key=None):
"""
List the public properties of an object, optionally
filtered by a `key` function
"""
props = [p for p in dir(obj) if p[0] is not '_']
if key is not None:
props = [p for p in props if key(p)]
return props
def PT(obj, key=None):
"""
List an object's public properties in a 4-col table,
optionally filtered by a `key` function
"""
_p = P(obj, key=key)
if filter is not None:
_max_w = max(len(p) for p in _p)
print('\n'.join(''.join(n.ljust(ceil((_max_w/4 + 2)*4)) for n in c) for c in chunk(4, P(obj))))
These functions are all centered around a problem I had during my coursework. The instructors gave us starter code, and we had to add on to it to complete the assignment. The starter code used a library I hadn't encountered before, and that library's documentation was less than thorough, so I used these methods to do a lot of manual digging into the objects it created and passed around.
The cool part about this file, though, is that you can add whatever you find useful to it, build it up over the years, sync it to a Git repo, whatever!
Debugging with pdb
Another tool I used a lot over this weekend was pdb
, the debugger built into Python. It has its own REPL, and I was frustrated to find that the pdb
REPL doesn't pay any attention to the PYTHONSTARTUP variable. However, I was very happy to find that it actually does have a default config file, ~/.pdbrc
!
Since I had my custom functions in a separate module, all I had to do was import that module in my .pdbrc
, and I was set!
### enable tab completion
import rlcompleter
import pdb
pdb.Pdb.complete = rlcompleter.Completer(locals()).complete
### import custom python utilities
import sys
from os.path import expanduser
sys.path.append(expanduser('~'))
from custom_utils import *
Note that enabling tab-completion in pdb
is slightly different from the standard REPL, but StackOverflow came to my rescue!
What you got?
That's my setup and my little utilities, but I'd love to see yours! Leave any handy Python utility functions you use all the time in the comments!
Top comments (3)
Importing favored tools and having debugging-on-tap are both great tricks. Love the show-what's-there functions!
The Python REPL, however, is not where it's at. Primitive in 2018. Recommend you try IPython (an enormously improved interactive REPL). Or better yet, Jupyter, which is IPython plus a web-based interactive explore/develop environment. Comes in 2 flavors, Jupyter Notebooks is the mainstream, stable experience; Jupyter Lab is the next-gen interface just now emerging. These take a little getting used to, but they are beyond fantastic upgrades to the old REPL. Once you use them, you'll wonder how you ever made do in the original REPL.
Nice, I'll have to check it out! Do they work with pdb or something similar for debugging? I was working in an environment where an IDE was infeasible, so CLI was all I had to work with
Yes repl clearing away sometimes is a bit off. nice article. never thought of it!