Ken Bellows
Posted on February 19, 2019
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!
Posted on February 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.