Hello all! In this note I'd like to collect some useful aliases which you can use in your .pdbrc
file which you can place to your HOME folder or current dir.
So let's start from docstring of pdb module:
The debugger supports aliases, which can save typing. And
aliases can have parameters (see the alias help entry) which
allows one a certain level of adaptability to the context
under examination.
So what is an alias after all?
alias [name [command]]
Create an alias called _name_ that executes _command_. The
command must not be enclosed in quotes. Replaceable
parameters can be indicated by %1, %2, and so on, while %* is
replaced by all the parameters. If no command is given, the
current alias for name is shown. If no arguments are given,
all aliases are listed.
Aliases may be nested and can contain anything that can
be legally typed at the pdb prompt. Note that internal pdb
commands can be overridden by aliases. Such a command is then
hidden until the alias is removed. Aliasing is recursively
applied to the first word of the command line; all other
words in the line are left alone.
Pretty simple, right?
Yet so powerful. Next in documentation we ca see two examples:
# Print instance variables (usage "pi classInst")
alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k])
# Print instance variables in self
alias ps pi self
The second one shows that aliases could be recursively applied to the first word of the command line
meaning that alias ps pi self
will be expanded further. Each alias handled in Pdb.precmd
method which accepts one argument -- line
:
...
args = line.split()
while args[0] in self.aliases:
line = self.aliases[args[0]]
...
So what does first alias do?
Let's take a look. Write some code to provide a context to our tests.
class Spam():
"""Run Away! Run Away!"""
def __init__(self, ham):
self.ham = ham
def egg(self):
print(self.ham)
spam = Spam('ham')
spam.egg()
Run this file with pdb3
command, see the (Pdb)
prompt and enter l
which stands for list
:
pdb3 /tmp/spam.py
> /tmp/spam.py(1)<module>()
-> class Spam():
(Pdb) l
1 -> class Spam():
2 """Run Away! Run Away!"""
3 def __init__(self, ham):
4 self.ham = ham
5
6 def egg(self):
7 print(self.ham)
8
9
10 spam = Spam('ham')
11 spam.egg()
(Pdb)
Then enter n
two times, so we will be here:
(Pdb) l
6 def egg(self):
7 print(self.ham)
8
9
10 spam = Spam('ham')
11 -> spam.egg()
[EOF]
(Pdb)
Now let's create an alias:
(Pdb) alias for k in %1.__dict__.keys(): print("{}.{} = {}".format(%1, k, %1.__dict__[k]))
It's a slightly changed version of the alias from the documentation.
Now let's use it:
(Pdb) pi spam
<__main__.Spam object at 0x7effec0f3358>.ham = ham
(Pdb)
Hmm.. not much...
Let's try this:
(Pdb) pi Spam
<class '__main__.Spam'>.__module__ = __main__
<class '__main__.Spam'>.__doc__ = Run Away! Run Away!
<class '__main__.Spam'>.__init__ = <function Spam.__init__ at 0x7fd414c622f0>
<class '__main__.Spam'>.egg = <function Spam.egg at 0x7fd414c62268>
<class '__main__.Spam'>.__dict__ = <attribute '__dict__' of 'Spam' objects>
<class '__main__.Spam'>.__weakref__ = <attribute '__weakref__' of 'Spam' objects>
Yeah, much better!
Now let's try ps spam
:
(Pdb) ps spam
*** NameError: name 'self' is not defined
What do we have here is an usage of the alias out of context.
Let's check alias again to refresh it in mind:
(Pdb) alias ps
ps = pi self
And alias pi
(Pdb) alias pi
pi = for k in %1.__dict__.keys(): print("{}.{} = {}".format(%1, k, %1.__dict__[k]))
So pi
accepts one argument: %1
, and ps
tries to pass self
to that alias and fails because self
is not in context. Let's define it and try again:
(Pdb) self = Spam
(Pdb) ps
<class '__main__.Spam'>.__module__ = __main__
<class '__main__.Spam'>.__doc__ = Run Away! Run Away!
<class '__main__.Spam'>.__init__ = <function Spam.__init__ at 0x7fdbc40f1e18>
<class '__main__.Spam'>.egg = <function Spam.egg at 0x7fdbc40f1d90>
<class '__main__.Spam'>.__dict__ = <attribute '__dict__' of 'Spam' objects>
<class '__main__.Spam'>.__weakref__ = <attribute '__weakref__' of 'Spam' objects>
We cheated a bit here. As described in the documentation aliases are treated as commands typed directly into the prompt meaning that all needed ingredients must be prepared to digest. You can treat aliases as functions which accepts n variables which would be taken from locals
or globals
.
And where variable self
is usually defined? In the class methods.
Let's restart our session with restart
command (You can check all available commands typing help
into prompt).
You don't need worry about lost progress because restart
will handle everything automatically: all breakpoints and defined aliases will be there at your service.
(Pdb) restart
Restarting /tmp/spam.py with arguments:
/tmp/spam.py
> /tmp/spam.py(1)<module>()
-> class Spam():
(Pdb) l
1 -> class Spam():
2 """Run Away! Run Away!"""
3 def __init__(self, ham):
4 self.ham = ham
5
6 def egg(self):
7 print(self.ham)
8
9
10 spam = Spam('ham')
11 spam.egg()
(Pdb) b 7
Breakpoint 1 at /tmp/spam.py:7
(Pdb) c
> /tmp/spam.py(7)egg()
-> print(self.ham)
Now let's restart
, l
ist code, put b
reakpoint at line 7
and c
ontinue execution.
We end up at line 7 as we supposed to.
Let's check ps
:
(Pdb) ps
<__main__.Spam object at 0x7fc94d49cef0>.ham = ham
We do not get errors because self is already defined.
That was short demo of aliases usage in pdb.
Be aware that aliases are executed in the current context so they can clutter with your variables:
6 B-> def egg(self):
7 print(self.ham)
(Pdb) aaa=1
(Pdb) bbb=2
(Pdb) alias clutter aaa=bbb
(Pdb) locals()['aaa'], locals()['bbb']
(1,2)
(Pdb) clutter
(Pdb) locals()['aaa'], locals()['bbb']
(2,2)
After executing clutter
alias variable aaa
got new value. So be sure to use variable names which are unique, prefix them with ___
as an example.
And I've got some more to share:
kf = __tmp = dir(%1); __filter = [k for k in __tmp if '%2' in k]; print(__filter); del __tmp; del __filter
pi = for k in %1.__dict__.keys(): print("{}.{} = {}".format(%1, k, %1.__dict__[k]))
ps = pi self
source = import inspect; print(inspect.getsource(%1))
Please share your snippets if you have anything useful, let's learn together. Debugging is pretty important skill to master so do not hesitate to read documentation on Pdb and source code.
Thanks for reading!
Top comments (2)
Hello gravesli! Thanks for your comment. I've checked the repo and it looks good, but I'm not gonna review it because your messages you're bombarding a lot of users looks really not cool. Number of start you've got is not quality of your code. You're not your stars. You're not your code. I can only suggest you reading more documentation and source code of others. Thanks