Last active
June 9, 2024 09:51
-
-
Save akuhn/195b4ec4305e809af2074b07d4628b14 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# My patches for ipdb debugger. | |
# | |
# - Use file history | |
# - Magic lambda expressions | |
# - Safe assignments (avoid collision with commands) | |
# - Print assignments | |
# - ls command (a better dir) | |
# - dollar command (a better pinfo) | |
# - fix debug command | |
# | |
# How to install this? | |
# | |
# Save this file somewhere and import it with execfile(...) in your ~/.pdbrc file. | |
# | |
# Thank you for using my code! | |
def patch_ipdb(): | |
import os | |
import re | |
import sys | |
from IPython.terminal.debugger import TerminalPdb | |
from prompt_toolkit.history import FileHistory | |
precmd = TerminalPdb.precmd | |
cmdloop = TerminalPdb.cmdloop | |
lambda_pattern = re.compile('(\.[a-z]+\()([a-z]+:)') | |
assignment_pattern = re.compile('^([a-z][_a-z0-9]*) = ') | |
def precmd_prime(self, original_line): | |
line = precmd(self, original_line) | |
# Magic lambda expressions | |
line = lambda_pattern.sub('\\1lambda \\2', line) | |
# Safe assignments (avoid collision with commands) | |
assignment = assignment_pattern.match(line) | |
if assignment: | |
line = "!{}".format(line) | |
# Print assignments | |
if assignment and not line.endswith(';'): | |
self.cmdqueue.append("!" + assignment.group(1)) | |
# if original_line != line: | |
# print '------>', line | |
# for cmd in self.cmdqueue: | |
# print '------>', cmd | |
return line | |
def cmdloop_prime(self, intro=None): | |
fname = os.path.join(os.path.expanduser('~'), '.ipdb_history') | |
self._pt_app.buffer.history = FileHistory(fname) | |
self._pt_app.buffer.reset() | |
self.do_alias('$ dollar %*') | |
cmdloop(self, intro) | |
TerminalPdb.precmd = precmd_prime | |
TerminalPdb.cmdloop = cmdloop_prime | |
# A better dir function. | |
# | |
# Prints names in columns and orders them by superclass. | |
# | |
def do_ls(self, arg): | |
try: | |
value = self._getval(arg) | |
except: | |
return | |
import fcntl, termios, struct | |
th, tw, hp, wp = struct.unpack('HHHH', fcntl.ioctl( | |
0, | |
termios.TIOCGWINSZ, | |
struct.pack('HHHH', 0, 0, 0, 0), | |
)) | |
terminal_width = tw | |
def printable_name(each): | |
try: | |
return 'instance' if each == value and not isinstance(value, type) else each.__name__ | |
except: | |
return each | |
def print_columns(names): | |
for n in range(len(names)): | |
num_rows = n + 1 | |
columns = [names[i:i + num_rows] for i in range(0, len(names), num_rows)] | |
widths = [max(len(name) for name in column) for column in columns] | |
total_width = sum(w + 2 for w in widths) | |
if total_width < terminal_width: | |
for row_num in range(num_rows): | |
for column_num in range(len(columns)): | |
if row_num < len(columns[column_num]): | |
print columns[column_num][row_num].ljust(widths[column_num] + 1), | |
print '' | |
return | |
ancestors = (type.mro(value) if isinstance(value, type) else [value] + type(value).mro())[::-1] | |
method_resolution = {} | |
for each in ancestors: | |
if not hasattr(each, '__dict__'): continue | |
for name in each.__dict__.keys(): | |
if name.startswith('__'): continue | |
method_resolution[name] = each | |
for each in ancestors: | |
if not hasattr(each, '__dict__'): continue | |
names = [name for name in each.__dict__.keys() if method_resolution.get(name) == each] | |
callables = [name for name in names if callable(each.__dict__[name])] | |
noncallables = [name for name in names if not callable(each.__dict__[name])] | |
if callables: | |
print "\033[0;34m{}#methods\033[0m".format(printable_name(each)) | |
print_columns(sorted(callables)) | |
if noncallables: | |
print "\033[0;34m{}#attributes\033[0m".format(printable_name(each)) | |
print_columns(sorted(noncallables)) | |
def do_dollar(self, arg): | |
try: | |
value = self._getval(arg) | |
except: | |
return | |
namespaces = [('Interactive', { 'name': value })] | |
self.shell.find_line_magic('pinfo2')('name', namespaces=namespaces) | |
# Copied from superclass to create correct subclass instance | |
def do_debug(self, arg): | |
sys.settrace(None) | |
globals = self.curframe.f_globals | |
locals = self.curframe_locals | |
p = TerminalPdb(self.completekey, self.stdin, self.stdout) | |
print >>self.stdout, "ENTERING RECURSIVE DEBUGGER" | |
sys.call_tracing(p.run, (arg, globals, locals)) | |
print >>self.stdout, "LEAVING RECURSIVE DEBUGGER" | |
sys.settrace(self.trace_dispatch) | |
self.lastcmd = p.lastcmd | |
TerminalPdb.do_ls = do_ls | |
TerminalPdb.do_dollar = do_dollar | |
TerminalPdb.do_debug = do_debug | |
patch_ipdb() | |
del patch_ipdb | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any plans to make a python3 version of this?